<?php

namespace Drupal\eb\Drush\Commands;

use Drupal\eb\Drush\Commands\Traits\PreviewHelperTrait;
use Drupal\eb\Service\DefinitionFactory;
use Drupal\eb\Service\OperationBuilder;
use Drupal\eb\Service\OperationDataBuilderInterface;
use Drupal\eb\Service\OperationProcessor;
use Drupal\eb\Service\PreviewGenerator;
use Drupal\eb\Service\ValidationManager;
use Drupal\eb\Service\YamlParser;
use Drush\Attributes as CLI;
use Drush\Commands\DrushCommands;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Drush commands for Entity Builder import operations.
 */
final class EbImportCommands extends DrushCommands {

  use PreviewHelperTrait;

  /**
   * Constructor.
   *
   * @param \Drupal\eb\Service\YamlParser $yamlParser
   *   The YAML parser service.
   * @param \Drupal\eb\Service\OperationBuilder $operationBuilder
   *   The operation builder service.
   * @param \Drupal\eb\Service\OperationDataBuilderInterface $operationDataBuilder
   *   The operation data builder service.
   * @param \Drupal\eb\Service\ValidationManager $validationManager
   *   The validation manager service.
   * @param \Drupal\eb\Service\PreviewGenerator $previewGenerator
   *   The preview generator service.
   * @param \Drupal\eb\Service\OperationProcessor $operationProcessor
   *   The operation processor service.
   * @param \Drupal\eb\Service\DefinitionFactory $definitionFactory
   *   The definition factory service.
   */
  public function __construct(
    protected YamlParser $yamlParser,
    protected OperationBuilder $operationBuilder,
    protected OperationDataBuilderInterface $operationDataBuilder,
    protected ValidationManager $validationManager,
    protected PreviewGenerator $previewGenerator,
    protected OperationProcessor $operationProcessor,
    protected DefinitionFactory $definitionFactory,
  ) {
    parent::__construct();
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container): self {
    return new self(
      $container->get('eb.yaml_parser'),
      $container->get('eb.operation_builder'),
      $container->get('eb.operation_data_builder'),
      $container->get('eb.validation_manager'),
      $container->get('eb.preview_generator'),
      $container->get('eb.operation_processor'),
      $container->get('eb.definition_factory'),
    );
  }

  /**
   * Import entity architecture from a file.
   *
   * By default, imports create a definition entity that can be applied later.
   * Use --execute to immediately execute operations after saving.
   *
   * @param string $file
   *   The file path to import.
   * @param array $options
   *   Command options.
   */
  #[CLI\Command(name: 'eb:import', aliases: ['ebi'])]
  #[CLI\Argument(name: 'file', description: 'The YAML file path to import')]
  #[CLI\Option(name: 'execute', description: 'Execute operations immediately')]
  #[CLI\Option(name: 'force', description: 'Overwrite existing definition')]
  #[CLI\Option(name: 'full', description: 'Show detailed operations in preview')]
  #[CLI\Usage(name: 'eb:import definitions.yml', description: 'Import as definition')]
  #[CLI\Usage(name: 'eb:import definitions.yml --execute', description: 'Import and execute')]
  #[CLI\Usage(name: 'eb:import definitions.yml --force', description: 'Overwrite existing')]
  #[CLI\Usage(name: 'eb:import definitions.yml --execute --full', description: 'Execute with preview')]
  public function import(
    string $file,
    array $options = [
      'execute' => FALSE,
      'force' => FALSE,
      'full' => FALSE,
    ],
  ): int {
    if (!file_exists($file)) {
      $this->logger()->error(dt('File not found: @file', ['@file' => $file]));
      return self::EXIT_FAILURE;
    }

    $content = file_get_contents($file);

    if (empty($content)) {
      $this->logger()->error(dt('File is empty'));
      return self::EXIT_FAILURE;
    }

    // Validate file extension.
    if (!$this->yamlParser->validateExtension($file)) {
      $this->logger()->error(dt('Invalid file extension. Only .yml and .yaml files are supported.'));
      return self::EXIT_FAILURE;
    }

    // Parse content to definition data.
    try {
      $parsedData = $this->yamlParser->parse($content);
    }
    catch (\Exception $e) {
      $this->logger()->error(dt('Error parsing file: @message', ['@message' => $e->getMessage()]));
      return self::EXIT_FAILURE;
    }

    // Raw data is the same as parsed data (no normalization needed).
    $rawData = $parsedData;

    // Convert definition data to operation arrays.
    // OperationDataBuilder handles both core and extension definitions.
    if ($this->operationDataBuilder->isDefinitionFormat($parsedData)) {
      $operationsData = $this->operationDataBuilder->build($parsedData);
    }
    else {
      // Data is already in operation format (has 'operation' keys).
      $operationsData = $parsedData;
    }

    // Build operation plugin instances for validation.
    $operationObjects = $this->operationBuilder->buildBatch($operationsData);

    if (empty($operationObjects)) {
      $this->logger()->error(dt('No valid operations found'));
      return self::EXIT_FAILURE;
    }

    // Validate operations with batch context awareness.
    $batch_result = $this->validationManager->validateBatch($operationObjects);

    if (!$batch_result->isValid()) {
      foreach ($batch_result->getErrors() as $error) {
        $this->logger()->error($error['message']);
      }
      return self::EXIT_FAILURE;
    }

    $this->logger()->success(dt('Validation passed: @count operations', ['@count' => count($operationObjects)]));

    // Create or update definition entity using raw YAML data.
    $definition = $this->definitionFactory->createFromYaml($rawData, $file);
    $definitionId = $definition->id();

    // Check if definition already exists.
    $existingDefinition = $this->definitionFactory->loadDefinition($definitionId);
    if ($existingDefinition) {
      if (!$options['force']) {
        $this->logger()->error(dt('Definition "@id" already exists. Use --force to overwrite.', [
          '@id' => $definitionId,
        ]));
        return self::EXIT_FAILURE;
      }

      // Update existing definition.
      $definition = $this->definitionFactory->updateFromYaml($existingDefinition, $rawData);
      $this->output()->writeln(dt('Updating existing definition: @id', ['@id' => $definitionId]));
    }
    else {
      $this->output()->writeln(dt('Creating new definition: @id', ['@id' => $definitionId]));
    }

    // Display definition info.
    $this->output()->writeln('');
    $this->output()->writeln(dt('Definition ID: @id', ['@id' => $definition->id()]));
    $this->output()->writeln(dt('Label: @label', ['@label' => $definition->label()]));
    $this->output()->writeln(dt('Bundles: @count', ['@count' => $definition->getBundleCount()]));
    $this->output()->writeln(dt('Fields: @count', ['@count' => $definition->getFieldCount()]));
    $this->output()->writeln('');

    // Save the definition.
    try {
      $definition->save();
      $this->logger()->success(dt('Definition "@id" saved successfully.', ['@id' => $definitionId]));
    }
    catch (\Exception $e) {
      $this->logger()->error(dt('Failed to save definition: @error', ['@error' => $e->getMessage()]));
      return self::EXIT_FAILURE;
    }

    // If not executing, show instructions and exit.
    if (!$options['execute']) {
      $this->output()->writeln('');
      $this->output()->writeln(dt('To apply this definition, run:'));
      $this->output()->writeln(dt('  drush eb:apply @id', ['@id' => $definitionId]));
      $this->output()->writeln('');
      $this->output()->writeln(dt('Or visit: /admin/config/development/eb/definitions/@id/apply', [
        '@id' => $definitionId,
      ]));
      return self::EXIT_SUCCESS;
    }

    // Execute operations if --execute flag was provided.
    $this->output()->writeln('');
    $this->logger()->notice(dt('Executing @count operations...', ['@count' => count($operationObjects)]));

    // Show preview if not in quiet mode.
    if ($options['full']) {
      $this->displayVerbosePreview($operationObjects);
    }
    else {
      $this->displayImportSummaryPreview($operationObjects);
    }

    // Execute operations with definition context for rollback grouping.
    $context = ['definition_id' => $definition->id()];
    $results = $this->operationProcessor->executeBatch($operationObjects, TRUE, $context);

    $success_count = 0;
    $error_count = 0;

    foreach ($results as $result) {
      if ($result->isSuccess()) {
        $success_count++;
      }
      else {
        $error_count++;
        foreach ($result->getErrors() as $error) {
          $this->logger()->error($error);
        }
      }
    }

    if ($success_count > 0) {
      $this->logger()->success(dt('Successfully executed @count operations', ['@count' => $success_count]));
    }

    if ($error_count === 0) {
      // Mark definition as applied.
      $definition->markAsApplied();
      $definition->save();
      $this->logger()->success(dt('Definition "@id" marked as applied.', ['@id' => $definitionId]));
    }
    else {
      $this->logger()->error(dt('@count operations failed', ['@count' => $error_count]));
      return self::EXIT_FAILURE;
    }

    return self::EXIT_SUCCESS;
  }

  /**
   * Validate an entity architecture file.
   *
   * @param string $file
   *   The file path to validate.
   */
  #[CLI\Command(name: 'eb:validate', aliases: ['ebv'])]
  #[CLI\Argument(name: 'file', description: 'The YAML file path to validate')]
  #[CLI\Usage(name: 'eb:validate definitions.yml', description: 'Validate definitions.yml')]
  public function validate(string $file): int {
    if (!file_exists($file)) {
      $this->logger()->error(dt('File not found: @file', ['@file' => $file]));
      return self::EXIT_FAILURE;
    }

    $content = file_get_contents($file);

    // Validate file extension.
    if (!$this->yamlParser->validateExtension($file)) {
      $this->logger()->error(dt('Invalid file extension. Only .yml and .yaml files are supported.'));
      return self::EXIT_FAILURE;
    }

    // Parse content to definition data.
    try {
      $parsedData = $this->yamlParser->parse($content);
    }
    catch (\Exception $e) {
      $this->logger()->error(dt('Parsing error: @message', ['@message' => $e->getMessage()]));
      return self::EXIT_FAILURE;
    }

    // Convert definition data to operation arrays.
    if ($this->operationDataBuilder->isDefinitionFormat($parsedData)) {
      $operationsData = $this->operationDataBuilder->build($parsedData);
    }
    else {
      $operationsData = $parsedData;
    }

    // Build operation plugin instances.
    $operations = $this->operationBuilder->buildBatch($operationsData);

    if (empty($operations)) {
      $this->logger()->error(dt('No valid operations found'));
      return self::EXIT_FAILURE;
    }

    // Validate operations with batch context awareness.
    $batch_result = $this->validationManager->validateBatch($operations);

    if (!$batch_result->isValid()) {
      foreach ($batch_result->getErrors() as $error) {
        $this->logger()->error($error['message']);
      }
      return self::EXIT_FAILURE;
    }

    $this->logger()->success(dt('Validation passed: @count operations', ['@count' => count($operations)]));
    return self::EXIT_SUCCESS;
  }

  /**
   * Show preview of operations without executing.
   *
   * @param string $file
   *   The file path to preview.
   * @param array $options
   *   Command options.
   */
  #[CLI\Command(name: 'eb:preview', aliases: ['ebp'])]
  #[CLI\Argument(name: 'file', description: 'The YAML file path to preview')]
  #[CLI\Option(name: 'full', description: 'Show detailed list of all operations')]
  #[CLI\Usage(name: 'eb:preview definitions.yml', description: 'Preview operations from definitions.yml (summary view)')]
  #[CLI\Usage(name: 'eb:preview definitions.yml --full', description: 'Show detailed list of all operations')]
  public function preview(string $file, array $options = ['full' => FALSE]): int {
    if (!file_exists($file)) {
      $this->logger()->error(dt('File not found: @file', ['@file' => $file]));
      return self::EXIT_FAILURE;
    }

    $content = file_get_contents($file);

    // Validate file extension.
    if (!$this->yamlParser->validateExtension($file)) {
      $this->logger()->error(dt('Invalid file extension. Only .yml and .yaml files are supported.'));
      return self::EXIT_FAILURE;
    }

    try {
      $parsedData = $this->yamlParser->parse($content);

      // Convert definition data to operation arrays.
      if ($this->operationDataBuilder->isDefinitionFormat($parsedData)) {
        $operationsData = $this->operationDataBuilder->build($parsedData);
      }
      else {
        $operationsData = $parsedData;
      }

      $operations = $this->operationBuilder->buildBatch($operationsData);

      $this->logger()->notice(dt('Preview of @count operations:', ['@count' => count($operations)]));

      if ($options['full']) {
        $this->displayVerbosePreview($operations);
      }
      else {
        $this->displaySummaryPreview($operations);
      }
    }
    catch (\Exception $e) {
      $this->logger()->error(dt('Error: @message', ['@message' => $e->getMessage()]));
      return self::EXIT_FAILURE;
    }

    return self::EXIT_SUCCESS;
  }

}
