<?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\EntityTypeInterface;
use Drupal\Core\Entity\Routing\AdminHtmlRouteProvider;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\flowdrop_session\Access\FlowDropSessionMessageAccessControlHandler;
use Drupal\flowdrop_session\Constants\MessageStatus;
use Drupal\views\EntityViewsData;

/**
 * Defines the FlowDrop Session Message entity.
 *
 * A message in a session. Messages can be user inputs,
 * assistant responses, system notifications, or execution logs.
 *
 * @internal
 *   This class is not part of the public API and may change without notice.
 */
#[ContentEntityType(
  id: 'flowdrop_session_message',
  label: new TranslatableMarkup('Session Message'),
  label_collection: new TranslatableMarkup('Session Messages'),
  label_singular: new TranslatableMarkup('session message'),
  label_plural: new TranslatableMarkup('session messages'),
  entity_keys: [
    'id' => 'id',
    'uuid' => 'uuid',
    'label' => 'id',
  ],
  handlers: [
    'views_data' => EntityViewsData::class,
    'access' => FlowDropSessionMessageAccessControlHandler::class,
    'form' => [
      'delete' => ContentEntityDeleteForm::class,
    ],
    'route_provider' => [
      'html' => AdminHtmlRouteProvider::class,
    ],
  ],
  links: [
    'canonical' => '/admin/flowdrop/session-messages/{flowdrop_session_message}',
    'delete-form' => '/admin/flowdrop/session-messages/{flowdrop_session_message}/delete',
  ],
  admin_permission: 'administer flowdrop_session',
  base_table: 'flowdrop_session_message',
  label_count: [
    'singular' => '@count session message',
    'plural' => '@count session messages',
  ],
)]
class FlowDropSessionMessage extends ContentEntityBase implements FlowDropSessionMessageInterface {

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

    // Reference to the parent session.
    $fields['session_id'] = BaseFieldDefinition::create('entity_reference')
      ->setLabel(new TranslatableMarkup('Session'))
      ->setDescription(new TranslatableMarkup('The session this message belongs to.'))
      ->setRequired(TRUE)
      ->setSetting('target_type', 'flowdrop_session')
      ->setSetting('handler', 'default')
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);

    // Message role (user, assistant, system, log).
    $fields['role'] = BaseFieldDefinition::create('list_string')
      ->setLabel(new TranslatableMarkup('Role'))
      ->setDescription(new TranslatableMarkup('The role of the message sender.'))
      ->setRequired(TRUE)
      ->setDefaultValue('user')
      ->setSettings([
        'allowed_values' => [
          'user' => 'User',
          'assistant' => 'Assistant',
          'system' => 'System',
          'log' => 'Log',
        ],
      ])
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'list_default',
        'weight' => -4,
      ])
      ->setDisplayConfigurable('view', TRUE);

    // Message content.
    $fields['content'] = BaseFieldDefinition::create('string_long')
      ->setLabel(new TranslatableMarkup('Content'))
      ->setDescription(new TranslatableMarkup('The message content.'))
      ->setRequired(TRUE)
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'string',
        'weight' => 0,
      ])
      ->setDisplayConfigurable('view', TRUE);

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

    // Node ID (for log messages that relate to a specific node).
    $fields['node_id'] = BaseFieldDefinition::create('string')
      ->setLabel(new TranslatableMarkup('Node ID'))
      ->setDescription(new TranslatableMarkup('The associated workflow node ID, if applicable.'))
      ->setSetting('max_length', 255)
      ->setDisplayConfigurable('view', TRUE);

    // Message metadata (JSON field for additional data).
    $fields['metadata'] = BaseFieldDefinition::create('string_long')
      ->setLabel(new TranslatableMarkup('Metadata'))
      ->setDescription(new TranslatableMarkup('Additional message metadata in JSON format.'))
      ->setDefaultValue('{}')
      ->setDisplayConfigurable('view', TRUE);

    // Message status (pending, processing, completed, failed, cancelled).
    $fields['status'] = BaseFieldDefinition::create('list_string')
      ->setLabel(new TranslatableMarkup('Status'))
      ->setDescription(new TranslatableMarkup('The processing status of the message.'))
      ->setRequired(TRUE)
      ->setDefaultValue(MessageStatus::PENDING)
      ->setSettings([
        'allowed_values' => MessageStatus::allowedValues(),
      ])
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'list_default',
        'weight' => -3,
      ])
      ->setDisplayConfigurable('view', TRUE);

    // Sequence number for ordering messages within a session.
    $fields['sequence_number'] = BaseFieldDefinition::create('integer')
      ->setLabel(new TranslatableMarkup('Sequence Number'))
      ->setDescription(new TranslatableMarkup('Sequential number for ordering messages within a session.'))
      ->setRequired(TRUE)
      ->setDefaultValue(0)
      ->setDisplayOptions('view', [
        'label' => 'inline',
        'type' => 'number_integer',
        'weight' => -2,
      ])
      ->setDisplayConfigurable('view', TRUE);

    // Parent message ID (for linking user message to assistant response).
    $fields['parent_message_id'] = BaseFieldDefinition::create('entity_reference')
      ->setLabel(new TranslatableMarkup('Parent Message'))
      ->setDescription(new TranslatableMarkup('The parent message this message responds to.'))
      ->setSetting('target_type', 'flowdrop_session_message')
      ->setSetting('handler', 'default')
      ->setDisplayConfigurable('view', TRUE);

    // Execution ID (links to workflow execution/snapshot).
    $fields['execution_id'] = BaseFieldDefinition::create('string')
      ->setLabel(new TranslatableMarkup('Execution ID'))
      ->setDescription(new TranslatableMarkup('The workflow execution ID that generated this message.'))
      ->setSetting('max_length', 255)
      ->setDisplayConfigurable('view', TRUE);

    // Retry count for failed message processing.
    $fields['retry_count'] = BaseFieldDefinition::create('integer')
      ->setLabel(new TranslatableMarkup('Retry Count'))
      ->setDescription(new TranslatableMarkup('Number of times this message has been retried.'))
      ->setDefaultValue(0)
      ->setDisplayConfigurable('view', TRUE);

    return $fields;
  }

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

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

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

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

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

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

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

  /**
   * {@inheritdoc}
   */
  public function getTimestamp(): int {
    $value = $this->get('timestamp')->value;
    return $value !== NULL ? (int) $value : 0;
  }

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

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

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

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

  /**
   * {@inheritdoc}
   */
  public function isUserMessage(): bool {
    return $this->getRole() === 'user';
  }

  /**
   * {@inheritdoc}
   */
  public function isAssistantMessage(): bool {
    return $this->getRole() === 'assistant';
  }

  /**
   * {@inheritdoc}
   */
  public function isSystemMessage(): bool {
    return $this->getRole() === 'system';
  }

  /**
   * {@inheritdoc}
   */
  public function isLogMessage(): bool {
    return $this->getRole() === 'log';
  }

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

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

  /**
   * {@inheritdoc}
   */
  public function isPending(): bool {
    return $this->getStatus() === MessageStatus::PENDING;
  }

  /**
   * {@inheritdoc}
   */
  public function isProcessing(): bool {
    return $this->getStatus() === MessageStatus::PROCESSING;
  }

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

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

  /**
   * {@inheritdoc}
   */
  public function isCancelled(): bool {
    return $this->getStatus() === MessageStatus::CANCELLED;
  }

  /**
   * {@inheritdoc}
   */
  public function isTerminal(): bool {
    return in_array($this->getStatus(), [MessageStatus::COMPLETED, MessageStatus::FAILED, MessageStatus::CANCELLED], TRUE);
  }

  /**
   * {@inheritdoc}
   */
  public function getSequenceNumber(): int {
    $value = $this->get('sequence_number')->value;
    return $value !== NULL ? (int) $value : 0;
  }

  /**
   * {@inheritdoc}
   */
  public function setSequenceNumber(int $sequence_number): static {
    $this->set('sequence_number', $sequence_number);
    return $this;
  }

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

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

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

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

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

  /**
   * {@inheritdoc}
   */
  public function getRetryCount(): int {
    $value = $this->get('retry_count')->value;
    return $value !== NULL ? (int) $value : 0;
  }

  /**
   * {@inheritdoc}
   */
  public function setRetryCount(int $retry_count): static {
    $this->set('retry_count', $retry_count);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function incrementRetryCount(): static {
    $this->setRetryCount($this->getRetryCount() + 1);
    return $this;
  }

}
