<?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 managing menus in NodeHive Spaces.
 */
#[Mcp(
  id: 'menu-manager-tool',
  name: new TranslatableMarkup('Menu Manager Tool'),
  description: new TranslatableMarkup('Provides MCP tools for creating and managing menus and menu links in NodeHive Spaces.'),
)]
class MenuManagerTool 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;

  /**
   * {@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');

    return $instance;
  }

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

  /**
   * {@inheritdoc}
   */
  public function buildConfigurationForm(
    array $form,
    FormStateInterface $form_state,
  ): array {
    // No specific configuration needed for this tool.
    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function getTools(): array {
    return [
      new Tool(
        name: 'create-menu',
        description: 'Create a new menu and optionally assign it to a NodeHive space.',
        inputSchema: [
          'type' => 'object',
          'properties' => [
            'menu_id' => [
              'type' => 'string',
              'description' => 'The machine name for the menu (e.g., "main-navigation").',
              'pattern' => '^[a-z0-9_-]+$',
            ],
            'label' => [
              'type' => 'string',
              'description' => 'The human-readable label for the menu.',
            ],
            'description' => [
              'type' => 'string',
              'description' => 'Optional description for the menu.',
            ],
            'space_id' => [
              'type' => 'integer',
              'description' => 'Optional: ID of the space to assign this menu to.',
            ],
          ],
          'required' => ['menu_id', 'label'],
        ],
      ),
      new Tool(
        name: 'add-menu-link',
        description: 'Add a new menu link to an existing menu.',
        inputSchema: [
          'type' => 'object',
          'properties' => [
            'menu_name' => [
              'type' => 'string',
              'description' => 'The machine name of the menu to add the link to.',
            ],
            'title' => [
              'type' => 'string',
              'description' => 'The title/label of the menu link.',
            ],
            'link_url' => [
              'type' => 'string',
              'description' => 'The URL for the menu link (e.g., "/about", "https://example.com", or "internal:/node/1").',
            ],
            'parent_link_uuid' => [
              'type' => 'string',
              'description' => 'Optional: UUID of the parent menu link for creating sub-menu items.',
            ],
            'weight' => [
              'type' => 'integer',
              'description' => 'Optional: Weight for ordering (default: 0).',
              'default' => 0,
            ],
            'expanded' => [
              'type' => 'boolean',
              'description' => 'Optional: Whether the menu link should be expanded by default (default: false).',
              'default' => FALSE,
            ],
            'enabled' => [
              'type' => 'boolean',
              'description' => 'Optional: Whether the menu link is enabled (default: true).',
              'default' => TRUE,
            ],
            'description' => [
              'type' => 'string',
              'description' => 'Optional: Description for the menu link.',
            ],
          ],
          'required' => ['menu_name', 'title', 'link_url'],
        ],
      ),
      new Tool(
        name: 'update-menu-link',
        description: 'Update an existing menu link.',
        inputSchema: [
          'type' => 'object',
          'properties' => [
            'menu_link_id' => [
              'type' => 'integer',
              'description' => 'The ID of the menu link to update.',
            ],
            'title' => [
              'type' => 'string',
              'description' => 'Optional: New title/label for the menu link.',
            ],
            'link_url' => [
              'type' => 'string',
              'description' => 'Optional: New URL for the menu link.',
            ],
            'weight' => [
              'type' => 'integer',
              'description' => 'Optional: New weight for ordering.',
            ],
            'expanded' => [
              'type' => 'boolean',
              'description' => 'Optional: Whether the menu link should be expanded.',
            ],
            'enabled' => [
              'type' => 'boolean',
              'description' => 'Optional: Whether the menu link is enabled.',
            ],
            'description' => [
              'type' => 'string',
              'description' => 'Optional: New description for the menu link.',
            ],
          ],
          'required' => ['menu_link_id'],
        ],
      ),
      new Tool(
        name: 'list-space-menus',
        description: 'List all menus associated with a specific NodeHive space.',
        inputSchema: [
          'type' => 'object',
          'properties' => [
            'space_id' => [
              'type' => 'integer',
              'description' => 'The ID of the space to list menus for.',
            ],
            'include_menu_links' => [
              'type' => 'boolean',
              'description' => 'Optional: Whether to include menu links in the response (default: true).',
              'default' => TRUE,
            ],
          ],
          'required' => ['space_id'],
        ],
      ),
      new Tool(
        name: 'assign-menu-to-space',
        description: 'Assign an existing menu to a NodeHive space.',
        inputSchema: [
          'type' => 'object',
          'properties' => [
            'menu_id' => [
              'type' => 'string',
              'description' => 'The machine name of the menu to assign.',
            ],
            'space_id' => [
              'type' => 'integer',
              'description' => 'The ID of the space to assign the menu to.',
            ],
          ],
          'required' => ['menu_id', 'space_id'],
        ],
      ),
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function executeTool(string $toolId, mixed $arguments): array {
    switch ($toolId) {
      case md5('create-menu'):
        return $this->executeCreateMenu($arguments);

      case md5('add-menu-link'):
        return $this->executeAddMenuLink($arguments);

      case md5('update-menu-link'):
        return $this->executeUpdateMenuLink($arguments);

      case md5('list-space-menus'):
        return $this->executeListSpaceMenus($arguments);

      case md5('assign-menu-to-space'):
        return $this->executeAssignMenuToSpace($arguments);

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

  /**
   * Execute the create menu tool.
   */
  protected function executeCreateMenu(array $arguments): array {
    try {
      // Validate required fields.
      if (empty($arguments['menu_id'])) {
        throw new \InvalidArgumentException('Menu ID is required.');
      }
      if (empty($arguments['label'])) {
        throw new \InvalidArgumentException('Menu label is required.');
      }

      // Check if menu already exists.
      $menu_storage = $this->entityTypeManager->getStorage('menu');
      $existing_menu = $menu_storage->load($arguments['menu_id']);
      if ($existing_menu) {
        throw new \InvalidArgumentException('Menu with ID "' . $arguments['menu_id'] . '" already exists.');
      }

      // Validate space if provided.
      $space = NULL;
      if (!empty($arguments['space_id'])) {
        $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.');
        }
      }

      // Create the menu.
      $menu_data = [
        'id' => $arguments['menu_id'],
        'label' => $arguments['label'],
        'description' => $arguments['description'] ?? '',
      ];

      $menu = $menu_storage->create($menu_data);
      $menu->save();

      // Assign menu to space if specified.
      if ($space) {
        $space->get('menu')->appendItem($menu);
        $space->save();
      }

      $result_text = sprintf(
        "Successfully created menu:\n• ID: %s\n• Label: %s\n• Description: %s",
        $menu->id(),
        $menu->label(),
        $menu->get('description')
      );

      if ($space) {
        $result_text .= sprintf("\n• Assigned to space: %s (ID: %d)", $space->label(), $space->id());
      }

      return [
        [
          'type' => 'text',
          'text' => $result_text,
        ],
      ];

    }
    catch (\Exception $e) {
      return [
        [
          'type' => 'text',
          'text' => 'Error creating menu: ' . $e->getMessage(),
        ],
      ];
    }
  }

  /**
   * Execute the add menu link tool.
   */
  protected function executeAddMenuLink(array $arguments): array {
    try {
      // Validate required fields.
      if (empty($arguments['menu_name'])) {
        throw new \InvalidArgumentException('Menu name is required.');
      }
      if (empty($arguments['title'])) {
        throw new \InvalidArgumentException('Title is required.');
      }
      if (empty($arguments['link_url'])) {
        throw new \InvalidArgumentException('Link URL is required.');
      }

      // Validate menu exists.
      $menu_storage = $this->entityTypeManager->getStorage('menu');
      $menu = $menu_storage->load($arguments['menu_name']);
      if (!$menu) {
        throw new \InvalidArgumentException('Menu "' . $arguments['menu_name'] . '" does not exist.');
      }

      // Validate parent link if provided.
      $parent_value = '';
      if (!empty($arguments['parent_link_uuid'])) {
        $menu_link_storage = $this->entityTypeManager->getStorage('menu_link_content');
        $parent_links = $menu_link_storage->loadByProperties(['uuid' => $arguments['parent_link_uuid']]);
        if (empty($parent_links)) {
          throw new \InvalidArgumentException('Parent menu link with UUID "' . $arguments['parent_link_uuid'] . '" not found.');
        }
        $parent_link = reset($parent_links);
        $parent_value = 'menu_link_content:' . $parent_link->uuid();
      }

      // Normalize URL format.
      $link_url = $arguments['link_url'];
      if (!str_starts_with($link_url, 'http') && !str_starts_with($link_url, 'internal:') && !str_starts_with($link_url, 'entity:')) {
        $link_url = 'internal:' . $link_url;
      }

      // Create the menu link.
      $menu_link_data = [
        'title' => $arguments['title'],
        'link' => ['uri' => $link_url],
        'menu_name' => $arguments['menu_name'],
        'weight' => $arguments['weight'] ?? 0,
        'expanded' => $arguments['expanded'] ?? FALSE,
        'enabled' => $arguments['enabled'] ?? TRUE,
        'description' => $arguments['description'] ?? '',
      ];

      if ($parent_value) {
        $menu_link_data['parent'] = $parent_value;
      }

      $menu_link_storage = $this->entityTypeManager->getStorage('menu_link_content');
      $menu_link = $menu_link_storage->create($menu_link_data);
      $menu_link->save();

      $result_text = sprintf(
        "Successfully created menu link:\n• ID: %d\n• Title: %s\n• URL: %s\n• Menu: %s\n• Weight: %d\n• Enabled: %s",
        $menu_link->id(),
        $menu_link->getTitle(),
        $menu_link->getUrlObject()->toString(),
        $menu_link->getMenuName(),
        $menu_link->getWeight(),
        $menu_link->isEnabled() ? 'Yes' : 'No'
      );

      if ($parent_value) {
        $result_text .= "\n• Parent: " . $arguments['parent_link_uuid'];
      }

      return [
        [
          'type' => 'text',
          'text' => $result_text,
        ],
      ];

    }
    catch (\Exception $e) {
      return [
        [
          'type' => 'text',
          'text' => 'Error creating menu link: ' . $e->getMessage(),
        ],
      ];
    }
  }

  /**
   * Execute the update menu link tool.
   */
  protected function executeUpdateMenuLink(array $arguments): array {
    try {
      // Validate required fields.
      if (empty($arguments['menu_link_id'])) {
        throw new \InvalidArgumentException('Menu link ID is required.');
      }

      // Load the menu link.
      $menu_link_storage = $this->entityTypeManager->getStorage('menu_link_content');
      $menu_link = $menu_link_storage->load($arguments['menu_link_id']);
      if (!$menu_link) {
        throw new \InvalidArgumentException('Menu link with ID ' . $arguments['menu_link_id'] . ' not found.');
      }

      $updated_fields = [];

      // Update title if provided.
      if (isset($arguments['title'])) {
        $menu_link->set('title', $arguments['title']);
        $updated_fields[] = 'title';
      }

      // Update link URL if provided.
      if (isset($arguments['link_url'])) {
        $link_url = $arguments['link_url'];
        if (!str_starts_with($link_url, 'http') && !str_starts_with($link_url, 'internal:') && !str_starts_with($link_url, 'entity:')) {
          $link_url = 'internal:' . $link_url;
        }
        $menu_link->set('link', ['uri' => $link_url]);
        $updated_fields[] = 'link URL';
      }

      // Update weight if provided.
      if (isset($arguments['weight'])) {
        $menu_link->set('weight', $arguments['weight']);
        $updated_fields[] = 'weight';
      }

      // Update expanded if provided.
      if (isset($arguments['expanded'])) {
        $menu_link->set('expanded', $arguments['expanded']);
        $updated_fields[] = 'expanded';
      }

      // Update enabled if provided.
      if (isset($arguments['enabled'])) {
        $menu_link->set('enabled', $arguments['enabled']);
        $updated_fields[] = 'enabled';
      }

      // Update description if provided.
      if (isset($arguments['description'])) {
        $menu_link->set('description', $arguments['description']);
        $updated_fields[] = 'description';
      }

      if (empty($updated_fields)) {
        throw new \InvalidArgumentException('At least one field must be provided to update.');
      }

      $menu_link->save();

      $result_text = sprintf(
        "Successfully updated menu link (ID: %d):\n• Updated fields: %s\n• Current title: %s\n• Current URL: %s\n• Current weight: %d",
        $menu_link->id(),
        implode(', ', $updated_fields),
        $menu_link->getTitle(),
        $menu_link->getUrlObject()->toString(),
        $menu_link->getWeight()
      );

      return [
        [
          'type' => 'text',
          'text' => $result_text,
        ],
      ];

    }
    catch (\Exception $e) {
      return [
        [
          'type' => 'text',
          'text' => 'Error updating menu link: ' . $e->getMessage(),
        ],
      ];
    }
  }

  /**
   * Execute the list space menus tool.
   */
  protected function executeListSpaceMenus(array $arguments): array {
    try {
      // Validate required fields.
      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_links = $arguments['include_menu_links'] ?? TRUE;

      // Get menus associated with the space.
      $menus_data = [];
      if (!$space->get('menu')->isEmpty()) {
        foreach ($space->get('menu')->referencedEntities() as $menu) {
          $menu_info = [
            'id' => $menu->id(),
            'label' => $menu->label(),
            'description' => $menu->get('description'),
          ];

          if ($include_links) {
            $menu_info['links'] = $this->getMenuLinks($menu->id());
          }

          $menus_data[] = $menu_info;
        }
      }

      if (empty($menus_data)) {
        $result_text = sprintf(
          "Space \"%s\" (ID: %d) has no menus assigned.",
          $space->label(),
          $space->id()
        );
      }
      else {
        $result_text = sprintf(
          "Menus for space \"%s\" (ID: %d):\n\n%s",
          $space->label(),
          $space->id(),
          $this->formatMenusList($menus_data, $include_links)
        );
      }

      return [
        [
          'type' => 'text',
          'text' => $result_text,
        ],
      ];

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

  /**
   * Execute the assign menu to space tool.
   */
  protected function executeAssignMenuToSpace(array $arguments): array {
    try {
      // Validate required fields.
      if (empty($arguments['menu_id'])) {
        throw new \InvalidArgumentException('Menu ID is required.');
      }
      if (empty($arguments['space_id'])) {
        throw new \InvalidArgumentException('Space ID is required.');
      }

      // Load the menu.
      $menu_storage = $this->entityTypeManager->getStorage('menu');
      $menu = $menu_storage->load($arguments['menu_id']);
      if (!$menu) {
        throw new \InvalidArgumentException('Menu "' . $arguments['menu_id'] . '" does not exist.');
      }

      // 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.');
      }

      // Check if menu is already assigned to the space.
      $existing_menus = [];
      if (!$space->get('menu')->isEmpty()) {
        foreach ($space->get('menu')->referencedEntities() as $existing_menu) {
          $existing_menus[] = $existing_menu->id();
        }
      }

      if (in_array($arguments['menu_id'], $existing_menus)) {
        throw new \InvalidArgumentException('Menu "' . $arguments['menu_id'] . '" is already assigned to this space.');
      }

      // Assign the menu to the space.
      $space->get('menu')->appendItem($menu);
      $space->save();

      $result_text = sprintf(
        "Successfully assigned menu \"%s\" (ID: %s) to space \"%s\" (ID: %d).",
        $menu->label(),
        $menu->id(),
        $space->label(),
        $space->id()
      );

      return [
        [
          'type' => 'text',
          'text' => $result_text,
        ],
      ];

    }
    catch (\Exception $e) {
      return [
        [
          'type' => 'text',
          'text' => 'Error assigning menu to space: ' . $e->getMessage(),
        ],
      ];
    }
  }

  /**
   * Get menu links for a specific menu.
   */
  protected function getMenuLinks(string $menu_name): array {
    $menu_link_storage = $this->entityTypeManager->getStorage('menu_link_content');
    $query = $menu_link_storage->getQuery()
      ->condition('menu_name', $menu_name)
      ->sort('weight')
      ->accessCheck(FALSE);

    $link_ids = $query->execute();
    $links = $menu_link_storage->loadMultiple($link_ids);

    $links_data = [];
    foreach ($links as $link) {
      $links_data[] = [
        'id' => $link->id(),
        'uuid' => $link->uuid(),
        'title' => $link->getTitle(),
        'url' => $link->getUrlObject()->toString(),
        'weight' => $link->getWeight(),
        'enabled' => $link->isEnabled(),
        'expanded' => $link->isExpanded(),
        'description' => $link->getDescription(),
        'parent' => $link->getParentId(),
      ];
    }

    return $links_data;
  }

  /**
   * Format menus list for display.
   */
  protected function formatMenusList(array $menus_data, bool $include_links): string {
    $output = [];
    foreach ($menus_data as $menu) {
      $menu_text = sprintf(
        "• %s (ID: %s)\n  Description: %s",
        $menu['label'],
        $menu['id'],
        $menu['description'] ?: 'None'
      );

      if ($include_links && !empty($menu['links'])) {
        $menu_text .= "\n  Links:";
        foreach ($menu['links'] as $link) {
          $status = $link['enabled'] ? 'Enabled' : 'Disabled';
          $menu_text .= sprintf(
            "\n    - %s (ID: %d) → %s [%s, Weight: %d]",
            $link['title'],
            $link['id'],
            $link['url'],
            $status,
            $link['weight']
          );
        }
      }
      elseif ($include_links) {
        $menu_text .= "\n  Links: None";
      }

      $output[] = $menu_text;
    }

    return implode("\n\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('menu_ui') &&
           $this->moduleHandler->moduleExists('menu_link_content');
  }

}
