<?php

declare(strict_types=1);

namespace Drupal\flowdrop_playground\Controller;

use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Datetime\DateFormatterInterface;
use Drupal\Core\Render\HtmlResponse;
use Drupal\Core\Url;
use Drupal\flowdrop_node_type\Service\NodeMetadataResolver;
use Drupal\flowdrop_session\Entity\FlowDropSessionInterface;
use Drupal\flowdrop_session\Service\SessionServiceInterface;
use Drupal\flowdrop_ui_components\Helper\UiConfigBuilder;
use Drupal\flowdrop_ui_components\Render\BarePageRenderer;
use Drupal\flowdrop_ui_components\Service\FlowDropEndpointConfigService;
use Drupal\flowdrop_workflow\Entity\FlowDropWorkflow;
use Drupal\flowdrop_workflow\Service\NavbarMenuBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Controller for FlowDrop Playground UI pages.
 *
 * @internal This class is not part of the public API and may change without notice.
 */
class PlaygroundController extends ControllerBase {

  public function __construct(
    protected readonly FlowDropEndpointConfigService $endpointConfigService,
    protected readonly SessionServiceInterface $sessionService,
    protected readonly NavbarMenuBuilder $navbarMenuBuilder,
    protected readonly BarePageRenderer $barePageRenderer,
    protected readonly DateFormatterInterface $dateFormatter,
    protected readonly NodeMetadataResolver $metadataResolver,
  ) {}

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container): static {
    return new static(
      $container->get('flowdrop_ui_components.endpoint_config'),
      $container->get('flowdrop_session.service'),
      $container->get('flowdrop_workflow.navbar_menu_builder'),
      $container->get('flowdrop_ui_components.bare_page_renderer'),
      $container->get('date.formatter'),
      $container->get('flowdrop_node_type.metadata_resolver'),
    );
  }

  /**
   * Lists sessions belonging to a workflow.
   */
  public function sessionsList(FlowDropWorkflow $flowdrop_workflow): array {
    $sessions = $this->sessionService->getSessionsForWorkflow(
      $flowdrop_workflow->id(),
      NULL,
      100,
    );

    $rows = [];
    foreach ($sessions as $session) {
      $sessionUrl = Url::fromRoute('flowdrop_playground.workflow.session.page', [
        'flowdrop_workflow' => $flowdrop_workflow->id(),
        'flowdrop_session' => $session->id(),
      ]);
      $owner = $session->getOwner();
      $rows[] = [
        'data' => [
          'name' => $sessionUrl->access()
            ? ['data' => ['#type' => 'link', '#title' => $session->getDisplayLabel(), '#url' => $sessionUrl]]
            : $session->getDisplayLabel(),
          'status' => $session->getStatus(),
          'owner' => $owner !== NULL ? $owner->getDisplayName() : $this->t('Anonymous'),
          'created' => $this->dateFormatter->format((int) $session->get('created')->value, 'short'),
          'changed' => $this->dateFormatter->format($session->getChangedTime(), 'short'),
        ],
      ];
    }

    $build = [];
    $build['table'] = [
      '#type' => 'table',
      '#header' => [
        'name' => $this->t('Name'),
        'status' => $this->t('Status'),
        'owner' => $this->t('Owner'),
        'created' => $this->t('Created'),
        'changed' => $this->t('Updated'),
      ],
      '#rows' => $rows,
      '#empty' => $this->t('No sessions yet for this workflow.'),
    ];
    $build['#cache'] = [
      'tags' => ['flowdrop_session_list'],
      'contexts' => ['user'],
    ];
    return $build;
  }

  /**
   * Title for the sessions list page.
   */
  public function sessionsListTitle(FlowDropWorkflow $flowdrop_workflow): string {
    return $this->t('Playground Sessions - @name', ['@name' => $flowdrop_workflow->label()])->__toString();
  }

  /**
   * Renders the playground for a single session.
   *
   * Delivered as a bare, theme-free full-page document (see BarePageRenderer)
   * so the app runs in the same clean CSS environment as the standalone
   * library demo, without Drupal's admin chrome bleeding in.
   */
  public function sessionPage(
    FlowDropWorkflow $flowdrop_workflow,
    FlowDropSessionInterface $flowdrop_session,
  ): HtmlResponse {
    if ($flowdrop_session->getWorkflowId() !== $flowdrop_workflow->id()) {
      throw new NotFoundHttpException();
    }

    return $this->renderSessionPlayground($flowdrop_workflow, $flowdrop_session);
  }

  /**
   * Title for the single-session playground page.
   */
  public function sessionPageTitle(
    FlowDropWorkflow $flowdrop_workflow,
    FlowDropSessionInterface $flowdrop_session,
  ): string {
    return $this->t('@workflow - @session', [
      '@workflow' => $flowdrop_workflow->label(),
      '@session' => $flowdrop_session->getDisplayLabel(),
    ])->__toString();
  }

  /**
   * Renders the SDC playground component pinned to a single session.
   */
  protected function renderSessionPlayground(
    FlowDropWorkflow $flowdrop_workflow,
    FlowDropSessionInterface $flowdrop_session,
  ): HtmlResponse {
    $workflow_data = [
      'id' => $flowdrop_workflow->id(),
      'label' => $flowdrop_workflow->label(),
      'description' => $flowdrop_workflow->getDescription(),
      // Enrich on read: stored nodes carry only the slim node-type anchor, so
      // re-inject the derived metadata the playground graph needs to render.
      'nodes' => $this->metadataResolver->enrichNodes($flowdrop_workflow->getNodes()),
      'edges' => $flowdrop_workflow->getEdges(),
      'metadata' => $flowdrop_workflow->getMetadata(),
      'created' => $flowdrop_workflow->getCreated(),
      'changed' => $flowdrop_workflow->getChanged(),
    ];

    $url_options = [
      'absolute' => TRUE,
      'language' => $this->languageManager()->getCurrentLanguage(),
    ];
    $site_url = Url::fromRoute('<front>', [], $url_options)->toString();
    $endpoint_config = $this->endpointConfigService->generateAbsoluteEndpointConfig($site_url);

    // Pin the playground's "list sessions" call to exactly this session.
    //
    // The Svelte app calls `listSessions(workflowId)` on mount to populate the
    // in-app session switcher in ControlPanel. On a per-session URL that
    // switcher must not let the user jump to a sibling session, so we override
    // the listSessions endpoint with an `?ids=` filter that returns only the
    // current session. The library shallow-merges Drupal's
    // `endpoints.playground` over its own defaults, so the override block has
    // to include every key in the playground group — otherwise the other
    // defaults would be dropped.
    $endpoint_config['endpoints']['playground'] = [
      'listSessions' => '/workflows/{id}/playground/sessions?ids=' . $flowdrop_session->id(),
      'createSession' => '/workflows/{id}/playground/sessions',
      'getSession' => '/playground/sessions/{sessionId}',
      'deleteSession' => '/playground/sessions/{sessionId}',
      'getMessages' => '/playground/sessions/{sessionId}/messages',
      'sendMessage' => '/playground/sessions/{sessionId}/messages',
      'stopExecution' => '/playground/sessions/{sessionId}/stop',
    ];

    // Back URL lands on the workflow's sessions list — its natural parent now
    // that each session has its own URL.
    $back_url = Url::fromRoute('flowdrop_playground.workflow.sessions', [
      'flowdrop_workflow' => $flowdrop_workflow->id(),
    ])->toString();

    // Pull tabs from the sessions list route so the navbar inside the bare
    // page shows the same Editor / Playground / Pipelines tab tree the user
    // sees on the surrounding admin pages. Actions are scoped to the single
    // session route — that's how "Back to sessions" surfaces here without
    // appearing on the list itself.
    $navbar_tasks = $this->navbarMenuBuilder->localTasks('flowdrop_playground.workflow.sessions');
    $navbar_actions = $this->navbarMenuBuilder->localActions('flowdrop_playground.workflow.session.page');

    $has_chat_input = $this->sessionService->hasChatInputNode($flowdrop_workflow);
    $config = $this->config("flowdrop_playground.settings");
    $auto_run_enabled = $config->get("auto_run") ?? TRUE;
    $auto_run = $auto_run_enabled && !$has_chat_input;

    $ui_config = UiConfigBuilder::fromConfig(
      $this->config('flowdrop_ui_components.settings'),
    );

    $props = [
      'workflow' => $workflow_data,
      'endpoint_config' => $endpoint_config,
      'container_id' => 'flowdrop-playground-' . $flowdrop_workflow->id(),
      'mode' => 'standalone',
      'height' => '100vh',
      'width' => '100vw',
      'back_url' => $back_url,
      'navbar_tasks' => $navbar_tasks,
      'navbar_actions' => $navbar_actions,
      'workflow_name' => $flowdrop_workflow->label(),
      'show_navbar' => TRUE,
      'navbar_title' => $flowdrop_session->getDisplayLabel(),
      'show_chat_input' => $has_chat_input,
      'show_run_button' => TRUE,
      'auto_run' => $auto_run,
      'ui_config' => $ui_config,
      'initial_session_id' => $flowdrop_session->uuid(),
      // Per-session URL: keep the session label visible (the chip), but lock
      // the user to this session — no create button, no list of siblings in
      // the popover. Pairs with the `?ids=` endpoint override above so even
      // the underlying data never reaches the browser.
      'show_session_header' => TRUE,
      'show_new_session_button' => FALSE,
      'show_session_list' => FALSE,
    ];

    $content = [
      '#type' => 'component',
      '#component' => 'flowdrop_ui_components:playground',
      '#props' => $props,
      '#attached' => [
        'library' => [
          'flowdrop_playground/playground',
        ],
      ],
      '#cache' => [
        'contexts' => ['route', 'user.permissions'],
        'tags' => $flowdrop_session->getCacheTags(),
      ],
    ];

    return $this->barePageRenderer->render($content, (string) $flowdrop_workflow->label());
  }

}
