<?php

namespace Drupal\eb\Form;

use Drupal\Core\Datetime\DateFormatterInterface;
use Drupal\Core\Form\ConfirmFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\eb\Entity\EbRollbackInterface;
use Drupal\eb\Service\RollbackManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Form for executing rollback operations.
 *
 * Executes a rollback for an entire definition apply - all operations
 * are rolled back in reverse order (definition-level granularity).
 */
class EbRollbackForm extends ConfirmFormBase {

  /**
   * The rollback ID.
   *
   * @var int
   */
  protected int $rollbackId;

  /**
   * The rollback entity.
   *
   * @var \Drupal\eb\Entity\EbRollbackInterface|null
   */
  protected ?EbRollbackInterface $rollback = NULL;

  /**
   * Constructor.
   *
   * @param \Drupal\eb\Service\RollbackManagerInterface $rollbackManager
   *   The rollback manager service.
   * @param \Drupal\Core\Datetime\DateFormatterInterface $dateFormatter
   *   The date formatter service.
   */
  public function __construct(
    protected RollbackManagerInterface $rollbackManager,
    protected DateFormatterInterface $dateFormatter,
  ) {}

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container): self {
    return new self(
      $container->get('eb.rollback_manager'),
      $container->get('date.formatter'),
    );
  }

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

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state, ?string $rollback_id = NULL): array {
    if ($rollback_id === NULL || !is_numeric($rollback_id)) {
      $this->messenger()->addError($this->t('Invalid rollback ID.'));
      return [];
    }

    $this->rollbackId = (int) $rollback_id;

    // Load rollback entity.
    $this->rollback = $this->rollbackManager->loadRollback($this->rollbackId);

    if ($this->rollback === NULL) {
      $this->messenger()->addError($this->t('Rollback operation not found.'));
      return [];
    }

    if (!$this->rollback->isPending()) {
      $this->messenger()->addWarning($this->t('This rollback has already been executed (status: @status).', [
        '@status' => $this->rollback->getStatus(),
      ]));
      return [];
    }

    $form['rollback_info'] = [
      '#type' => 'details',
      '#title' => $this->t('Rollback Information'),
      '#open' => TRUE,
    ];

    $form['rollback_info']['label'] = [
      '#markup' => '<p><strong>' . $this->t('Label:') . '</strong> ' . $this->rollback->label() . '</p>',
    ];

    $form['rollback_info']['definition'] = [
      '#markup' => '<p><strong>' . $this->t('Definition ID:') . '</strong> ' . ($this->rollback->getDefinitionId() ?: '-') . '</p>',
    ];

    $form['rollback_info']['operations'] = [
      '#markup' => '<p><strong>' . $this->t('Operations:') . '</strong> ' . $this->rollback->getOperationCount() . '</p>',
    ];

    $created = $this->rollback->get('created')->value;
    $form['rollback_info']['created'] = [
      '#markup' => '<p><strong>' . $this->t('Created:') . '</strong> ' .
      ($created ? $this->dateFormatter->format((int) $created, 'long') : '-') . '</p>',
    ];

    $owner = $this->rollback->getOwner();
    $form['rollback_info']['user'] = [
      '#markup' => '<p><strong>' . $this->t('User:') . '</strong> ' .
      ($owner ? $owner->getDisplayName() : $this->t('Unknown')) . '</p>',
    ];

    $form['rollback_info']['warning'] = [
      '#markup' => '<div class="messages messages--warning">' .
      $this->t('Warning: This will undo ALL @count operations from this definition apply in reverse order. This action cannot be undone.', [
        '@count' => $this->rollback->getOperationCount(),
      ]) .
      '</div>',
    ];

    // Load and display operations.
    $operations = $this->rollbackManager->getOperationsForRollback($this->rollbackId);

    if (!empty($operations)) {
      // Sort by sequence descending (rollback order).
      usort($operations, fn($a, $b) => $b->getSequence() <=> $a->getSequence());

      // Build summary by operation type.
      $type_counts = [];
      foreach ($operations as $operation) {
        $type = $operation->getOperationType();
        $type_counts[$type] = ($type_counts[$type] ?? 0) + 1;
      }

      $form['operation_summary'] = [
        '#type' => 'details',
        '#title' => $this->t('Operation Summary'),
        '#open' => TRUE,
      ];

      $summary_items = [];
      foreach ($type_counts as $type => $count) {
        $summary_items[] = $this->t('@type: @count', ['@type' => $type, '@count' => $count]);
      }
      $form['operation_summary']['list'] = [
        '#theme' => 'item_list',
        '#items' => $summary_items,
      ];

      // Build detailed operations table.
      $form['operations'] = [
        '#type' => 'details',
        '#title' => $this->t('Operations to Rollback (@count)', ['@count' => count($operations)]),
        '#open' => FALSE,
      ];

      $form['operations']['list'] = [
        '#type' => 'container',
      ];

      $index = count($operations);
      foreach ($operations as $operation) {
        $data = $operation->getOriginalData();
        $op_id = $operation->id();

        $form['operations']['list'][$op_id] = [
          '#type' => 'details',
          '#title' => $this->t('#@num @type: @target', [
            '@num' => $index,
            '@type' => $operation->getOperationType(),
            '@target' => $this->extractTarget($operation->getOperationType(), $data) ?: $this->extractBundle($data),
          ]),
          '#open' => FALSE,
        ];

        $form['operations']['list'][$op_id]['summary'] = [
          '#type' => 'container',
          '#attributes' => ['class' => ['operation-summary']],
        ];

        $form['operations']['list'][$op_id]['summary']['entity_type'] = [
          '#markup' => '<p><strong>' . $this->t('Entity Type:') . '</strong> ' . ($this->extractEntityType($data) ?: '-') . '</p>',
        ];

        $form['operations']['list'][$op_id]['summary']['bundle'] = [
          '#markup' => '<p><strong>' . $this->t('Bundle:') . '</strong> ' . ($this->extractBundle($data) ?: '-') . '</p>',
        ];

        $form['operations']['list'][$op_id]['data'] = [
          '#type' => 'details',
          '#title' => $this->t('Raw Data'),
          '#open' => FALSE,
        ];

        $form['operations']['list'][$op_id]['data']['content'] = [
          '#type' => 'html_tag',
          '#tag' => 'pre',
          '#value' => json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES),
          '#attributes' => [
            'style' => 'background: #f5f5f5; padding: 10px; overflow: auto; max-height: 300px;',
          ],
        ];

        $index--;
      }
    }

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

  /**
   * {@inheritdoc}
   */
  public function getQuestion() {
    return $this->t('Are you sure you want to execute this rollback?');
  }

  /**
   * {@inheritdoc}
   */
  public function getCancelUrl() {
    return new Url('entity.eb_rollback.collection');
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state): void {
    try {
      $result = $this->rollbackManager->executeRollback($this->rollbackId);

      if ($result->isSuccess()) {
        $this->messenger()->addStatus($this->t('Rollback executed successfully. All @count operations have been reversed.', [
          '@count' => $this->rollback?->getOperationCount() ?? 0,
        ]));
      }
      else {
        $this->messenger()->addError($this->t('Rollback failed: @message', [
          '@message' => implode(', ', $result->getErrors()),
        ]));
      }
    }
    catch (\Exception $e) {
      $this->messenger()->addError($this->t('Error executing rollback: @message', [
        '@message' => $e->getMessage(),
      ]));
    }

    $form_state->setRedirectUrl($this->getCancelUrl());
  }

  /**
   * Extracts entity type from rollback data.
   *
   * @param array<string, mixed> $data
   *   The rollback data.
   *
   * @return string
   *   The entity type or empty string.
   */
  protected function extractEntityType(array $data): string {
    // Direct entity_type field.
    if (!empty($data['entity_type'])) {
      return $data['entity_type'];
    }

    // Extract from field_config_id (e.g., "node.blog_post.field_name").
    if (!empty($data['field_config_id'])) {
      $parts = explode('.', $data['field_config_id']);
      return $parts[0];
    }

    // Extract from storage_config_id (e.g., "node.field_name").
    if (!empty($data['storage_config_id'])) {
      $parts = explode('.', $data['storage_config_id']);
      return $parts[0];
    }

    return '';
  }

  /**
   * Extracts bundle from rollback data.
   *
   * @param array<string, mixed> $data
   *   The rollback data.
   *
   * @return string
   *   The bundle or empty string.
   */
  protected function extractBundle(array $data): string {
    // Direct bundle field.
    if (!empty($data['bundle'])) {
      return $data['bundle'];
    }

    // Direct bundle_id field (for create_bundle operations).
    if (!empty($data['bundle_id'])) {
      return $data['bundle_id'];
    }

    // Extract from field_config_id (e.g., "node.blog_post.field_name").
    if (!empty($data['field_config_id'])) {
      $parts = explode('.', $data['field_config_id']);
      return $parts[1] ?? '';
    }

    return '';
  }

  /**
   * Extracts target identifier from rollback data based on operation type.
   *
   * @param string $operationType
   *   The operation type.
   * @param array<string, mixed> $data
   *   The rollback data.
   *
   * @return string
   *   The target identifier.
   */
  protected function extractTarget(string $operationType, array $data): string {
    switch ($operationType) {
      case 'create_bundle':
      case 'update_bundle':
      case 'delete_bundle':
        return $data['bundle_id'] ?? '';

      case 'create_field':
      case 'update_field':
      case 'delete_field':
        // Extract field name from field_config_id.
        if (!empty($data['field_config_id'])) {
          $parts = explode('.', $data['field_config_id']);
          return $parts[2] ?? '';
        }
        return $data['field_name'] ?? '';

      case 'configure_form_mode':
      case 'configure_view_mode':
      case 'hide_field':
        $field = $data['field_name'] ?? $data['field'] ?? '';
        $mode = $data['form_mode'] ?? $data['view_mode'] ?? $data['display_mode'] ?? 'default';
        return $field ? "$field ($mode)" : '';

      case 'create_field_group':
      case 'update_field_group':
      case 'delete_field_group':
        return $data['group_name'] ?? '';

      case 'create_menu':
      case 'delete_menu':
        return $data['menu_id'] ?? '';

      default:
        // Try common field names.
        return $data['id'] ?? $data['name'] ?? '';
    }
  }

}
