<?php

declare(strict_types=1);

namespace Drupal\native_observability_spans\Controller;

use Drupal\Core\Controller\ControllerBase;
use Drupal\native_observability\Service\TraceStorageInterface;
use Drupal\native_observability_spans\Service\SpanStorageInterface;
use Drupal\Core\Link;
use Drupal\Core\Url;

/**
 * Displays spans linked to a trace.
 *
 * IMPORTANT:
 * - We primarily load spans by request_id (stable correlation key).
 * - trace_id backfill is optional; we keep a fallback to loadByTraceId().
 */
final class TraceSpansController extends ControllerBase {

  public function __construct(
    private readonly TraceStorageInterface $traceStorage,
    private readonly SpanStorageInterface $spanStorage,
  ) {
  }

  /**
   * Builds the spans page for a given trace.
   *
   * @param int $id
   *   Trace numeric primary key.
   *
   * @return array
   *   Render array.
   */
  public function build(int $id): array {
    // Load the trace using the numeric primary key.
    $trace = $this->traceStorage->load($id);

    if (!$trace) {
      return [
        '#type' => 'container',
        'message' => [
          '#markup' => '<p><strong>' . $this->t('Trace not found.') . '</strong></p>',
        ],
      ];
    }

    // Spans are correlated via request_id.
    $request_id = (string) ($trace['request_id'] ?? '');

    $spans = [];
    if ($request_id !== '') {
      $spans = $this->spanStorage->loadByRequestId($request_id);
    }

    if (!$spans) {
      $spans = $this->spanStorage->loadByTraceId($id);
    }

    $header = [
      $this->t('Span ID'),
      $this->t('Parent'),
      $this->t('Name'),
      $this->t('Category'),
      $this->t('Duration (ms)'),
      $this->t('Started'),
      $this->t('Ended'),
    ];

    $rows = [];
    foreach ($spans as $span) {
      $rows[] = [
        (string) ($span['span_id'] ?? ''),
        (string) ($span['parent_span_id'] ?? ''),
        (string) ($span['name'] ?? ''),
        (string) ($span['category'] ?? ''),
        (string) ($span['duration_ms'] ?? ''),
        (string) ($span['started_at_ms'] ?? ''),
        (string) ($span['ended_at_ms'] ?? ''),
      ];
    }

    $back_link = Link::fromTextAndUrl(
      $this->t('Back to trace'),
      Url::fromRoute('native_observability.trace_view', ['id' => $id])
    )->toRenderable();

    $back_link['#attributes']['class'] = [
      'button',
      'button--secondary',
    ];

    $delete_link = Link::fromTextAndUrl(
      $this->t('Delete spans'),
      Url::fromRoute('native_observability_spans.trace_spans_delete', ['id' => $id])
    )->toRenderable();

    $delete_link['#attributes']['class'] = [
      'button',
      'button--danger',
    ];

    return [
      '#type' => 'container',
      '#attributes' => ['class' => ['native-observability-spans']],
      'actions' => [
        '#type' => 'actions',
        'back' => $back_link,
        'delete' => $delete_link,
      ],
      'meta' => [
        '#markup' => $this->t(
          '<p><strong>Trace:</strong> @id &nbsp; <strong>Request ID:</strong> @request_id</p>',
          [
            '@id' => (string) $id,
            '@request_id' => $request_id,
          ],
        ),
      ],
      'table' => [
        '#type' => 'table',
        '#header' => $header,
        '#rows' => $rows,
        '#empty' => $this->t('No spans found for this trace.'),
      ],
    ];
  }

}
