<?php

declare(strict_types=1);

namespace Drupal\flowdrop_session\Entity;

use Drupal\Component\Serialization\Json;
use Drupal\Core\Entity\Attribute\ContentEntityType;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\ContentEntityDeleteForm;
use Drupal\Core\Entity\EntityChangedTrait;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\Routing\AdminHtmlRouteProvider;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\flowdrop_session\Access\FlowDropSessionAccessControlHandler;
use Drupal\flowdrop_session\Constants\SessionStatus;
use Drupal\flowdrop_session\FlowDropSessionListBuilder;
use Drupal\flowdrop_session\Form\FlowDropSessionForm;
use Drupal\flowdrop_workflow\FlowDropWorkflowInterface;
use Drupal\user\EntityOwnerTrait;
use Drupal\views\EntityViewsData;

/**
 * Defines the FlowDrop Session entity.
 *
 * A session represents an isolated execution environment for a workflow.
 * Sessions maintain conversation history and allow interactive execution.
 *
 * @internal
 *   This class is not part of the public API and may change without notice.
 */
#[ContentEntityType(
  id: 'flowdrop_session',
  label: new TranslatableMarkup('Session'),
  label_collection: new TranslatableMarkup('Sessions'),
  label_singular: new TranslatableMarkup('session'),
  label_plural: new TranslatableMarkup('sessions'),
  entity_keys: [
    'id' => 'id',
    'uuid' => 'uuid',
    'label' => 'name',
    'owner' => 'uid',
  ],
  handlers: [
    'list_builder' => FlowDropSessionListBuilder::class,
    'views_data' => EntityViewsData::class,
    'access' => FlowDropSessionAccessControlHandler::class,
    'form' => [
      'add' => FlowDropSessionForm::class,
      'edit' => FlowDropSessionForm::class,
      'delete' => ContentEntityDeleteForm::class,
    ],
    'route_provider' => [
      'html' => AdminHtmlRouteProvider::class,
    ],
  ],
  links: [
    'collection' => '/admin/flowdrop/sessions',
    'canonical' => '/admin/flowdrop/sessions/{flowdrop_session}',
    'edit-form' => '/admin/flowdrop/sessions/{flowdrop_session}/edit',
    'delete-form' => '/admin/flowdrop/sessions/{flowdrop_session}/delete',
  ],
  admin_permission: 'administer flowdrop_session',
  base_table: 'flowdrop_session',
  label_count: [
    'singular' => '@count session',
    'plural' => '@count sessions',
  ],
)]
class FlowDropSession extends ContentEntityBase implements FlowDropSessionInterface {

  use EntityChangedTrait;
  use EntityOwnerTrait;

  /**
   * {@inheritdoc}
   */
  public static function baseFieldDefinitions(EntityTypeInterface $entity_type): array {
    $fields = parent::baseFieldDefinitions($entity_type);

    // Session name/label.
    $fields['name'] = BaseFieldDefinition::create('string')
      ->setLabel(new TranslatableMarkup('Name'))
      ->setDescription(new TranslatableMarkup('The session display name.'))
      ->setRequired(TRUE)
      ->setSetting('max_length', 255)
      ->setDisplayOptions('form', [
        'type' => 'string_textfield',
        'weight' => -5,
      ])
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayOptions('view', [
        'label' => 'hidden',
        'type' => 'string',
        'weight' => -5,
      ])
      ->setDisplayConfigurable('view', TRUE);

    // Reference to the workflow.
    $fields['workflow_id'] = BaseFieldDefinition::create('entity_reference')
      ->setLabel(new TranslatableMarkup('Workflow'))
      ->setDescription(new TranslatableMarkup('The workflow this session is testing.'))
      ->setRequired(TRUE)
      ->setSetting('target_type', 'flowdrop_workflow')
      ->setSetting('handler', 'default')
      ->setDisplayOptions('form', [
        'type' => 'entity_reference_autocomplete',
        'weight' => 5,
        'settings' => [
          'match_operator' => 'CONTAINS',
          'size' => '60',
          'placeholder' => '',
        ],
      ])
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);

    // Session status.
    $fields['status'] = BaseFieldDefinition::create('list_string')
      ->setLabel(new TranslatableMarkup('Status'))
      ->setDescription(new TranslatableMarkup('The current status of the session.'))
      ->setRequired(TRUE)
      ->setDefaultValue(SessionStatus::IDLE)
      ->setSettings([
        'allowed_values' => SessionStatus::allowedValues(),
      ])
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'list_default',
        'weight' => -4,
      ])
      ->setDisplayOptions('form', [
        'type' => 'options_select',
        'weight' => -4,
      ])
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);

    // Owner (user who created the session).
    $fields['uid'] = BaseFieldDefinition::create('entity_reference')
      ->setLabel(new TranslatableMarkup('Author'))
      ->setDescription(new TranslatableMarkup('The user who created this session.'))
      ->setSetting('target_type', 'user')
      ->setDefaultValueCallback(static::class . '::getDefaultEntityOwner')
      ->setDisplayOptions('form', [
        'type' => 'entity_reference_autocomplete',
        'settings' => [
          'match_operator' => 'CONTAINS',
          'size' => 60,
          'placeholder' => '',
        ],
        'weight' => 15,
      ])
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'author',
        'weight' => 15,
      ])
      ->setDisplayConfigurable('view', TRUE);

    // Creation timestamp.
    $fields['created'] = BaseFieldDefinition::create('created')
      ->setLabel(new TranslatableMarkup('Created'))
      ->setDescription(new TranslatableMarkup('The time when the session was created.'))
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'timestamp',
        'weight' => 20,
      ])
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);

    // Changed timestamp.
    $fields['changed'] = BaseFieldDefinition::create('changed')
      ->setLabel(new TranslatableMarkup('Changed'))
      ->setDescription(new TranslatableMarkup('The time when the session was last updated.'))
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);

    // Custom metadata (JSON field).
    $fields['metadata'] = BaseFieldDefinition::create('string_long')
      ->setLabel(new TranslatableMarkup('Metadata'))
      ->setDescription(new TranslatableMarkup('Custom session metadata in JSON format.'))
      ->setDefaultValue('{}')
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);

    // Current pipeline ID (if running).
    $fields['current_pipeline_id'] = BaseFieldDefinition::create('entity_reference')
      ->setLabel(new TranslatableMarkup('Current Pipeline'))
      ->setDescription(new TranslatableMarkup('The currently executing pipeline, if any.'))
      ->setSetting('target_type', 'flowdrop_pipeline')
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);

    // Execution mode (sync or async).
    $fields['execution_mode'] = BaseFieldDefinition::create('list_string')
      ->setLabel(new TranslatableMarkup('Execution Mode'))
      ->setDescription(new TranslatableMarkup('How workflows should be executed: synchronous (immediate) or asynchronous (queued).'))
      ->setRequired(TRUE)
      ->setDefaultValue('sync')
      ->setSettings([
        'allowed_values' => [
          'sync' => 'Synchronous (immediate)',
          'async' => 'Asynchronous (queued)',
        ],
      ])
      ->setDisplayOptions('form', [
        'type' => 'options_select',
        'weight' => 10,
      ])
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);

    // Orchestrator settings (JSON field for orchestrator configuration).
    $fields['orchestrator_settings'] = BaseFieldDefinition::create('string_long')
      ->setLabel(new TranslatableMarkup('Orchestrator Settings'))
      ->setDescription(new TranslatableMarkup('Orchestrator configuration in JSON format (overrides workflow defaults).'))
      ->setDefaultValue('{}')
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);

    return $fields;
  }

  /**
   * {@inheritdoc}
   */
  public function getName(): string {
    $value = $this->get('name')->value;
    return $value !== NULL ? (string) $value : '';
  }

  /**
   * {@inheritdoc}
   */
  public function setName(string $name): static {
    $this->set('name', $name);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getWorkflowId(): string {
    $target_id = $this->get('workflow_id')->target_id;
    return $target_id !== NULL ? (string) $target_id : '';
  }

  /**
   * {@inheritdoc}
   */
  public function setWorkflowId(string $workflow_id): static {
    $this->set('workflow_id', ['target_id' => $workflow_id]);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getWorkflow(): ?FlowDropWorkflowInterface {
    $entity = $this->get('workflow_id')->entity;
    return $entity instanceof FlowDropWorkflowInterface ? $entity : NULL;
  }

  /**
   * {@inheritdoc}
   */
  public function getStatus(): string {
    $value = $this->get('status')->value;
    return $value !== NULL ? (string) $value : SessionStatus::IDLE;
  }

  /**
   * {@inheritdoc}
   */
  public function setStatus(string $status): static {
    $this->set('status', $status);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getMetadata(): array {
    $data = $this->get('metadata')->value;
    if ($data === NULL || $data === '') {
      return [];
    }
    $decoded = Json::decode($data);
    return is_array($decoded) ? $decoded : [];
  }

  /**
   * {@inheritdoc}
   */
  public function setMetadata(array $metadata): static {
    $this->set('metadata', Json::encode($metadata));
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getCurrentPipelineId(): ?int {
    $target_id = $this->get('current_pipeline_id')->target_id;
    return $target_id !== NULL ? (int) $target_id : NULL;
  }

  /**
   * {@inheritdoc}
   */
  public function setCurrentPipelineId(?int $pipeline_id): static {
    if ($pipeline_id === NULL) {
      $this->set('current_pipeline_id', NULL);
    }
    else {
      $this->set('current_pipeline_id', ['target_id' => $pipeline_id]);
    }
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function isIdle(): bool {
    return $this->getStatus() === SessionStatus::IDLE;
  }

  /**
   * {@inheritdoc}
   */
  public function isRunning(): bool {
    return $this->getStatus() === SessionStatus::RUNNING;
  }

  /**
   * {@inheritdoc}
   */
  public function isCompleted(): bool {
    return $this->getStatus() === SessionStatus::COMPLETED;
  }

  /**
   * {@inheritdoc}
   */
  public function isFailed(): bool {
    return $this->getStatus() === SessionStatus::FAILED;
  }

  /**
   * {@inheritdoc}
   */
  public function preSave(EntityStorageInterface $storage): void {
    parent::preSave($storage);

    // Set default name if not provided.
    if ($this->isNew() && empty($this->getName())) {
      $workflow = $this->getWorkflow();
      $workflow_name = $workflow instanceof FlowDropWorkflowInterface
        ? $workflow->label()
        : 'Workflow';
      $this->setName("Session - {$workflow_name} - " . date('Y-m-d H:i'));
    }
  }

  /**
   * Gets the execution mode.
   *
   * @return string
   *   The execution mode: sync or async.
   */
  public function getExecutionMode(): string {
    $value = $this->get('execution_mode')->value;
    return $value !== NULL ? (string) $value : 'sync';
  }

  /**
   * Sets the execution mode.
   *
   * @param string $execution_mode
   *   The execution mode: sync or async.
   *
   * @return static
   *   The session entity.
   */
  public function setExecutionMode(string $execution_mode): static {
    $this->set('execution_mode', $execution_mode);
    return $this;
  }

  /**
   * Gets the orchestrator settings.
   *
   * @return array<string, mixed>
   *   The orchestrator settings array.
   */
  public function getOrchestratorSettings(): array {
    $data = $this->get('orchestrator_settings')->value;
    if ($data === NULL || $data === '') {
      return [];
    }
    $decoded = Json::decode($data);
    return is_array($decoded) ? $decoded : [];
  }

  /**
   * Sets the orchestrator settings.
   *
   * @param array<string, mixed> $settings
   *   The orchestrator settings array.
   *
   * @return static
   *   The session entity.
   */
  public function setOrchestratorSettings(array $settings): static {
    $this->set('orchestrator_settings', Json::encode($settings));
    return $this;
  }

}
