<?php

namespace Drupal\nodehive_mcp\Plugin\Mcp;

use Drupal\Core\Access\AccessResult;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\mcp\Attribute\Mcp;
use Drupal\mcp\Plugin\McpPluginBase;
use Drupal\mcp\ServerFeatures\Tool;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * MCP plugin for exploring NodeHive Spaces.
 */
#[Mcp(
  id: 'explore-space-tool',
  name: new TranslatableMarkup('Explore Space Tool'),
  description: new TranslatableMarkup('Provides MCP tool for exploring NodeHive Spaces in detail.'),
)]
class ExploreSpaceTool extends McpPluginBase implements ContainerFactoryPluginInterface {

  use StringTranslationTrait;

  /**
   * The entity type manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * The module handler.
   *
   * @var \Drupal\Core\Extension\ModuleHandlerInterface
   */
  protected $moduleHandler;

  /**
   * The menu link tree.
   *
   * @var \Drupal\Core\Menu\MenuLinkTreeInterface
   */
  protected $menuTree;

  /**
   * {@inheritDoc}
   */
  public static function create(
    ContainerInterface $container,
    array $configuration,
    $plugin_id,
    $plugin_definition,
  ) {
    $instance = parent::create(
      $container, $configuration, $plugin_id, $plugin_definition
    );
    $instance->entityTypeManager = $container->get('entity_type.manager');
    $instance->moduleHandler = $container->get('module_handler');
    $instance->menuTree = $container->get('menu.link_tree');

    return $instance;
  }

  /**
   * {@inheritdoc}
   */
  public function defaultConfiguration(): array {
    return [
      'enabled' => TRUE,
      'config' => [
        'default_content_limit' => 50,
      ],
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function buildConfigurationForm(
    array $form,
    FormStateInterface $form_state,
  ): array {
    $config = $this->getConfiguration();

    $form['default_content_limit'] = [
      '#type' => 'number',
      '#title' => $this->t('Default Content Limit'),
      '#description' => $this->t('Default number of content items to return in space exploration.'),
      '#default_value' => $config['config']['default_content_limit'] ?? 50,
      '#min' => 1,
      '#max' => 1000,
    ];

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function getTools(): array {
    $config = $this->getConfiguration();

    return [
      new Tool(
        name: 'explore-nodehive-space',
        description: 'Explore a NodeHive Space to get detailed overview including menus, content, and sitemap structure.',
        inputSchema: [
          'type' => 'object',
          'properties' => [
            'space_id' => [
              'type' => 'integer',
              'description' => 'The ID of the space to explore.',
            ],
            'include_content' => [
              'type' => 'boolean',
              'description' => 'Include detailed content information.',
              'default' => TRUE,
            ],
            'include_menus' => [
              'type' => 'boolean',
              'description' => 'Include menu structure and sitemap.',
              'default' => TRUE,
            ],
            'content_limit' => [
              'type' => 'integer',
              'description' => 'Maximum number of content items to return (default: 50).',
              'minimum' => 1,
              'default' => $config['config']['default_content_limit'] ?? 50,
            ],
          ],
          'required' => ['space_id'],
        ],
      ),
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function executeTool(string $toolId, mixed $arguments): array {
    if ($toolId === md5('explore-nodehive-space')) {
      return $this->executeExploreSpace($arguments);
    }

    throw new \InvalidArgumentException('Unknown tool: ' . $toolId);
  }

  /**
   * Execute the explore space tool.
   */
  protected function executeExploreSpace(array $arguments): array {
    try {
      $config = $this->getConfiguration();

      // Validate space ID.
      if (empty($arguments['space_id'])) {
        throw new \InvalidArgumentException('Space ID is required.');
      }

      // Load the space.
      $space_storage = $this->entityTypeManager->getStorage('nodehive_space');
      $space = $space_storage->load($arguments['space_id']);

      if (!$space) {
        throw new \InvalidArgumentException('Space with ID ' . $arguments['space_id'] . ' not found.');
      }

      $include_content = $arguments['include_content'] ?? TRUE;
      $include_menus = $arguments['include_menus'] ?? TRUE;
      $content_limit = $arguments['content_limit'] ?? $config['config']['default_content_limit'] ?? 50;

      $exploration_data = [
        'space_info' => $this->getSpaceInfo($space),
        'menus' => $include_menus ? $this->getSpaceMenus($space) : [],
        'content' => $include_content ? $this->getSpaceContent($space, $content_limit) : [],
        'statistics' => $this->getSpaceStatistics($space),
      ];

      return [
        [
          'type' => 'text',
          'text' => $this->formatSpaceExploration($exploration_data),
        ],
      ];
    }
    catch (\Exception $e) {
      return [
        [
          'type' => 'text',
          'text' => 'Error exploring space: ' . $e->getMessage(),
        ],
      ];
    }
  }

  /**
   * Get basic space information.
   */
  protected function getSpaceInfo($space): array {
    $space_info = [
      'id' => $space->id(),
      'label' => $space->label(),
      'space_url' => $space->get('space_url')->uri,
      'space_type' => $space->get('space_type')->value,
      'status' => (bool) $space->get('status')->value,
      'created' => date('Y-m-d H:i:s', $space->get('created')->value),
      'changed' => date('Y-m-d H:i:s', $space->get('changed')->value),
      'tags' => [],
    ];

    // Get tags.
    if (!$space->get('tags')->isEmpty()) {
      foreach ($space->get('tags')->referencedEntities() as $tag) {
        $space_info['tags'][] = $tag->label();
      }
    }

    // Get frontpage node if set.
    if (!$space->get('frontpage_node')->isEmpty()) {
      $frontpage_node = $space->get('frontpage_node')->entity;
      if ($frontpage_node) {
        $space_info['frontpage_node'] = [
          'id' => $frontpage_node->id(),
          'title' => $frontpage_node->getTitle(),
          'type' => $frontpage_node->bundle(),
        ];
      }
    }

    return $space_info;
  }

  /**
   * Get space menus and build sitemap structure.
   */
  protected function getSpaceMenus($space): array {
    $menus_data = [];

    // Get all menus and check which ones are linked to this space.
    $menu_storage = $this->entityTypeManager->getStorage('menu');
    $all_menus = $menu_storage->loadMultiple();

    foreach ($all_menus as $menu) {
      // Check if this menu is linked to the current space.
      $referenced_spaces = $menu->getThirdPartySetting('nodehive_core', 'nodehive_space_field', []);

      if (in_array($space->id(), $referenced_spaces)) {
        $menu_data = [
          'id' => $menu->id(),
          'label' => $menu->label(),
          'description' => $menu->getDescription(),
          'items' => $this->getMenuItems($menu->id()),
        ];
        $menus_data[] = $menu_data;
      }
    }

    return $menus_data;
  }

  /**
   * Get menu items in hierarchical structure.
   */
  protected function getMenuItems($menu_name, $parent = ''): array {
    $menu_tree = $this->menuTree;

    $parameters = $menu_tree->getCurrentRouteMenuTreeParameters($menu_name);
    $parameters->setRoot($parent)->expandedParents = [];
    $parameters->setMaxDepth(10);

    $tree = $menu_tree->load($menu_name, $parameters);
    $manipulators = [
      ['callable' => 'menu.default_tree_manipulators:checkAccess'],
      ['callable' => 'menu.default_tree_manipulators:generateIndexAndSort'],
    ];
    $tree = $menu_tree->transform($tree, $manipulators);

    return $this->buildMenuItemsArray($tree);
  }

  /**
   * Build menu items array from menu tree.
   */
  protected function buildMenuItemsArray($tree): array {
    $items = [];

    foreach ($tree as $element) {
      $link = $element->link;
      $item = [
        'title' => $link->getTitle(),
        'url' => $link->getUrlObject()->toString(),
        'description' => $link->getDescription(),
        'weight' => $link->getWeight(),
        'enabled' => $link->isEnabled(),
        'expanded' => $link->isExpanded(),
      ];

      if ($element->hasChildren) {
        $item['children'] = $this->buildMenuItemsArray($element->subtree);
      }

      $items[] = $item;
    }

    return $items;
  }

  /**
   * Get space content.
   */
  protected function getSpaceContent($space, $limit = 50): array {
    $content_data = [];

    $query = $this->entityTypeManager->getStorage('node')->getQuery()
      ->accessCheck(FALSE)
      ->condition('nodehive_space', $space->id());

    if ($limit) {
      $query->range(0, $limit);
    }

    $node_ids = $query->execute();
    $content_entities = $this->entityTypeManager->getStorage('node')->loadMultiple($node_ids);

    if (!empty($content_entities)) {
      foreach ($content_entities as $node) {
        $content_item = [
          'id' => $node->id(),
          'title' => $node->getTitle(),
          'type' => $node->bundle(),
          'status' => $node->isPublished() ? 'Published' : 'Unpublished',
          'created' => date('Y-m-d H:i:s', $node->getCreatedTime()),
          'changed' => date('Y-m-d H:i:s', $node->getChangedTime()),
          'author' => $node->getOwner()->getDisplayName(),
          'url' => $node->toUrl()->toString(),
        ];

        // Get summary/body if available.
        if ($node->hasField('body') && !$node->get('body')->isEmpty()) {
          $body = $node->get('body')->value;
          $content_item['summary'] = strlen($body) > 200 ? substr(strip_tags($body), 0, 200) . '...' : strip_tags($body);
        }

        $content_data[] = $content_item;
      }
    }

    return $content_data;
  }

  /**
   * Get space statistics.
   */
  protected function getSpaceStatistics($space): array {
    $stats = [
      'total_content' => 0,
      'total_menus' => 0,
      'content_by_type' => [],
      'published_content' => 0,
      'unpublished_content' => 0,
    ];

    // Count content.
    $node_ids = $this->entityTypeManager->getStorage('node')->getQuery()
      ->accessCheck(FALSE)
      ->condition('nodehive_space', $space->id())
      ->execute();
    $content_entities = $this->entityTypeManager->getStorage('node')->loadMultiple($node_ids);
    $stats['total_content'] = count($content_entities);

    foreach ($content_entities as $node) {
      $type = $node->bundle();
      if (!isset($stats['content_by_type'][$type])) {
        $stats['content_by_type'][$type] = 0;
      }
      $stats['content_by_type'][$type]++;

      if ($node->isPublished()) {
        $stats['published_content']++;
      }
      else {
        $stats['unpublished_content']++;
      }
    }

    // Count menus using the same approach as getSpaceMenus.
    $menu_storage = $this->entityTypeManager->getStorage('menu');
    $all_menus = $menu_storage->loadMultiple();

    foreach ($all_menus as $menu) {
      $referenced_spaces = $menu->getThirdPartySetting('nodehive_core', 'nodehive_space_field', []);
      if (in_array($space->id(), $referenced_spaces)) {
        $stats['total_menus']++;
      }
    }

    return $stats;
  }

  /**
   * Format space exploration data for display.
   */
  protected function formatSpaceExploration(array $data): string {
    $output = [];

    // Space Info.
    $space_info = $data['space_info'];
    $output[] = "=== SPACE OVERVIEW ===";
    $output[] = sprintf("Name: %s (ID: %d)", $space_info['label'], $space_info['id']);
    $output[] = sprintf("URL: %s", $space_info['space_url']);
    $output[] = sprintf("Type: %s | Status: %s", $space_info['space_type'], $space_info['status'] ? 'Enabled' : 'Disabled');

    if (!empty($space_info['tags'])) {
      $output[] = sprintf("Tags: %s", implode(', ', $space_info['tags']));
    }

    if (isset($space_info['frontpage_node'])) {
      $output[] = sprintf("Frontpage: %s (ID: %d, Type: %s)",
        $space_info['frontpage_node']['title'],
        $space_info['frontpage_node']['id'],
        $space_info['frontpage_node']['type']
      );
    }

    $output[] = sprintf("Created: %s | Last Modified: %s", $space_info['created'], $space_info['changed']);

    // Statistics.
    $stats = $data['statistics'];
    $output[] = "\n=== STATISTICS ===";
    $output[] = sprintf("Total Content: %d (%d published, %d unpublished)",
      $stats['total_content'],
      $stats['published_content'],
      $stats['unpublished_content']
    );
    $output[] = sprintf("Total Menus: %d", $stats['total_menus']);

    if (!empty($stats['content_by_type'])) {
      $output[] = "Content by Type:";
      foreach ($stats['content_by_type'] as $type => $count) {
        $output[] = sprintf("  - %s: %d", $type, $count);
      }
    }

    // Menus.
    if (!empty($data['menus'])) {
      $output[] = "\n=== MENUS & SITEMAP ===";
      foreach ($data['menus'] as $menu) {
        $output[] = sprintf("Menu: %s (ID: %s)", $menu['label'], $menu['id']);
        if ($menu['description']) {
          $output[] = sprintf("Description: %s", $menu['description']);
        }

        if (!empty($menu['items'])) {
          $output[] = "Menu Structure:";
          $output[] = $this->formatMenuItems($menu['items'], 1);
        }
        else {
          $output[] = "  (No menu items)";
        }
        $output[] = "";
      }
    }

    // Content.
    if (!empty($data['content'])) {
      $output[] = "\n=== CONTENT ===";
      foreach ($data['content'] as $content) {
        $output[] = sprintf("• %s (ID: %d)", $content['title'], $content['id']);
        $output[] = sprintf("  Type: %s | Status: %s | Author: %s",
          $content['type'],
          $content['status'],
          $content['author']
        );
        $output[] = sprintf("  URL: %s", $content['url']);
        $output[] = sprintf("  Created: %s | Modified: %s", $content['created'], $content['changed']);

        if (isset($content['summary'])) {
          $output[] = sprintf("  Summary: %s", $content['summary']);
        }
        $output[] = "";
      }
    }

    return implode("\n", $output);
  }

  /**
   * Format menu items hierarchically.
   */
  protected function formatMenuItems(array $items, int $depth = 0): string {
    $output = [];
    $indent = str_repeat("  ", $depth);

    foreach ($items as $item) {
      $status = $item['enabled'] ? '' : ' (disabled)';
      $expanded = $item['expanded'] ? ' [expanded]' : '';

      $output[] = sprintf("%s- %s%s%s", $indent, $item['title'], $status, $expanded);
      $output[] = sprintf("%s  URL: %s", $indent, $item['url']);

      if ($item['description']) {
        $output[] = sprintf("%s  Description: %s", $indent, $item['description']);
      }

      if (!empty($item['children'])) {
        $output[] = $this->formatMenuItems($item['children'], $depth + 1);
      }
    }

    return implode("\n", $output);
  }

  /**
   * {@inheritdoc}
   */
  public function hasAccess(): AccessResult {
    return AccessResult::allowedIfHasPermission(
      $this->currentUser, 'use nodehive mcp tools'
    );
  }

  /**
   * {@inheritdoc}
   */
  public function checkRequirements(): bool {
    return $this->moduleHandler->moduleExists('nodehive_core') &&
           $this->moduleHandler->moduleExists('mcp');
  }

}
