<?php

declare(strict_types=1);

namespace Drupal\flowdrop_chat\Form;

use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\flowdrop_ai_provider\Service\AiModelService;
use Drupal\flowdrop_chat\Service\ChatService;
use Drupal\flowdrop_memory\Service\MemoryBackendPluginManager;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Settings form for FlowDrop Chat module configuration.
 *
 * @internal
 */
final class ChatSettingsForm extends ConfigFormBase {

  /**
   * The AI model service.
   *
   * @var \Drupal\flowdrop_ai_provider\Service\AiModelService
   */
  private AiModelService $aiModelService;

  /**
   * The memory backend plugin manager.
   *
   * @var \Drupal\flowdrop_memory\Service\MemoryBackendPluginManager
   */
  private MemoryBackendPluginManager $backendPluginManager;

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

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container): static {
    $instance = parent::create($container);
    $instance->aiModelService = $container->get('flowdrop_ai_provider.model_service');
    $instance->backendPluginManager = $container->get('flowdrop_memory.backend_plugin_manager');
    $instance->entityTypeManager = $container->get('entity_type.manager');
    return $instance;
  }

  /**
   * {@inheritdoc}
   */
  protected function getEditableConfigNames(): array {
    return ['flowdrop_chat.settings'];
  }

  /**
   * {@inheritdoc}
   */
  public function getFormId(): string {
    return 'flowdrop_chat_settings_form';
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state): array {
    $config = $this->config('flowdrop_chat.settings');

    $form['llm_provider'] = [
      '#type' => 'select',
      '#title' => $this->t('LLM Provider'),
      '#description' => $this->t('The AI provider and model to use for chat completions. Leave empty to use the system default.'),
      '#options' => $this->buildModelOptions(),
      '#default_value' => $config->get('llm_provider') ?? '',
      '#empty_option' => $this->t('- System default -'),
    ];

    $form['system_prompt_template'] = [
      '#type' => 'textarea',
      '#title' => $this->t('System Prompt Template'),
      '#description' => $this->t('Template for the system prompt. Available placeholders: <code>{{AVAILABLE_NODE_TYPES}}</code>, <code>{{WORKFLOW_STATE}}</code>, <code>{{CHAT_HISTORY}}</code>, <code>{{USER_MESSAGE}}</code>. Leave empty to use the built-in DSL system prompt.'),
      '#default_value' => $config->get('system_prompt_template') ?? '',
      '#rows' => 10,
    ];

    $form['max_history_length'] = [
      '#type' => 'number',
      '#title' => $this->t('Max History Length'),
      '#description' => $this->t('Maximum number of messages to retain in chat history.'),
      '#default_value' => $config->get('max_history_length') ?? 50,
      '#min' => 1,
      '#max' => 500,
      '#required' => TRUE,
    ];

    $form['memory_backend'] = [
      '#type' => 'select',
      '#title' => $this->t('Memory Backend'),
      '#description' => $this->t('The memory backend to use for persisting chat history.'),
      '#options' => $this->buildBackendOptions(),
      '#default_value' => $config->get('memory_backend') ?? 'entity',
      '#required' => TRUE,
    ];

    $form['processing'] = [
      '#type' => 'details',
      '#title' => $this->t('Processing Mode'),
      '#open' => TRUE,
    ];

    $form['processing']['processing_mode'] = [
      '#type' => 'select',
      '#title' => $this->t('Processing Mode'),
      '#description' => $this->t('FlowDrop Builtin uses the built-in PHP pipeline. FlowDrop Workflow delegates message processing to a FlowDrop workflow for customisation.'),
      '#options' => [
        ChatService::MODE_BUILTIN => $this->t('FlowDrop Builtin'),
        ChatService::MODE_WORKFLOW => $this->t('FlowDrop Workflow'),
      ],
      '#default_value' => $config->get('processing_mode') ?: ChatService::MODE_BUILTIN,
      '#required' => TRUE,
    ];

    $form['processing']['processing_workflow'] = [
      '#type' => 'select',
      '#title' => $this->t('Processing Workflow'),
      '#description' => $this->t('The workflow to execute for chat message processing. The workflow must accept <code>message</code>, <code>workflow_state</code>, and <code>history</code> as inputs, and return a <code>content</code> output.'),
      '#options' => $this->buildWorkflowOptions(),
      '#default_value' => $config->get('processing_workflow') ?? '',
      '#empty_option' => $this->t('- Select a workflow -'),
      '#states' => [
        'visible' => [
          ':input[name="processing_mode"]' => ['value' => ChatService::MODE_WORKFLOW],
        ],
        'required' => [
          ':input[name="processing_mode"]' => ['value' => ChatService::MODE_WORKFLOW],
        ],
      ],
    ];

    return parent::buildForm($form, $form_state);
  }

  /**
   * {@inheritdoc}
   */
  public function validateForm(array &$form, FormStateInterface $form_state): void {
    parent::validateForm($form, $form_state);

    $max_history = $form_state->getValue('max_history_length');
    if (!is_numeric($max_history) || (int) $max_history < 1) {
      $form_state->setErrorByName('max_history_length', $this->t('Max history length must be a positive integer.'));
    }

    $mode = (string) ($form_state->getValue('processing_mode') ?? ChatService::MODE_BUILTIN);
    if ($mode === ChatService::MODE_WORKFLOW && empty($form_state->getValue('processing_workflow'))) {
      $form_state->setErrorByName('processing_workflow', $this->t('A processing workflow must be selected when using Workflow mode.'));
    }

    $template = (string) ($form_state->getValue('system_prompt_template') ?? '');
    if (!empty($template)) {
      $allowed = ['{{AVAILABLE_NODE_TYPES}}', '{{WORKFLOW_STATE}}', '{{CHAT_HISTORY}}', '{{USER_MESSAGE}}'];
      preg_match_all('/\{\{[A-Z_]+\}\}/', $template, $matches);
      $unknown = array_diff($matches[0], $allowed);
      if (!empty($unknown)) {
        $form_state->setErrorByName('system_prompt_template', $this->t(
          'Unknown placeholder(s) in template: @placeholders. Allowed: @allowed.',
          [
            '@placeholders' => implode(', ', $unknown),
            '@allowed' => implode(', ', $allowed),
          ]
        ));
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state): void {
    $this->config('flowdrop_chat.settings')
      ->set('llm_provider', $form_state->getValue('llm_provider') ?? '')
      ->set('system_prompt_template', $form_state->getValue('system_prompt_template') ?? '')
      ->set('max_history_length', (int) $form_state->getValue('max_history_length'))
      ->set('memory_backend', $form_state->getValue('memory_backend'))
      ->set('processing_mode', $form_state->getValue('processing_mode') ?? ChatService::MODE_BUILTIN)
      ->set('processing_workflow', $form_state->getValue('processing_workflow') ?? '')
      ->save();

    parent::submitForm($form, $form_state);
  }

  /**
   * Builds select options from available FlowDrop workflows.
   *
   * @return array<string, string>
   *   Options keyed by workflow ID with workflow labels.
   */
  private function buildWorkflowOptions(): array {
    $options = [];
    $workflows = $this->entityTypeManager->getStorage('flowdrop_workflow')->loadMultiple();
    foreach ($workflows as $id => $workflow) {
      $options[$id] = $workflow->label();
    }
    return $options;
  }

  /**
   * Builds select options from registered memory backend plugins.
   *
   * @return array<string, string>
   *   Options keyed by plugin ID with human-readable labels.
   */
  private function buildBackendOptions(): array {
    $options = [];
    foreach ($this->backendPluginManager->getDefinitions() as $id => $definition) {
      $options[$id] = (string) ($definition['label'] ?? $id);
    }
    return $options;
  }

  /**
   * Builds select options from available chat models.
   *
   * @return array<string, string>
   *   Options keyed by "provider:model_id" with human-readable labels.
   */
  private function buildModelOptions(): array {
    $options = [];
    $models = $this->aiModelService->getAvailableModels('chat');

    foreach ($models as $model_id => $model) {
      $key = $model['provider'] . ':' . $model_id;
      $options[$key] = $model['name'] . ' (' . $model['provider'] . ')';
    }

    return $options;
  }

}
