<?php

declare(strict_types=1);

namespace App\Framework\LiveComponents\Tracing;

use App\Framework\Telemetry\UnifiedTelemetryService;
use App\Framework\Telemetry\OperationHandle;
use App\Framework\Tracing\Tracer;
use App\Framework\Tracing\TraceSpan;
use App\Framework\Tracing\SpanStatus;

/**
 * LiveComponent-specific tracing integration with framework telemetry system.
 *
 * Provides standardized tracing for LiveComponent operations:
 * - livecomponent.resolve: Component initialization and data loading
 * - livecomponent.render: Component HTML rendering
 * - livecomponent.handle: Action handling and business logic
 * - livecomponent.upload: File upload processing
 */
final readonly class LiveComponentTracer
{
    public function __construct(
        private UnifiedTelemetryService $telemetry
    ) {}

    /**
     * Trace component resolution (initialization)
     *
     * @param string $componentId Component identifier
     * @param array<string, mixed> $attributes Additional trace attributes
     * @return OperationHandle Telemetry operation handle
     */
    public function traceResolve(string $componentId, array $attributes = []): OperationHandle
    {
        return $this->telemetry->startOperation(
            name: 'livecomponent.resolve',
            type: 'custom',
            attributes: array_merge([
                'component.id' => $componentId,
                'operation.type' => 'resolve',
            ], $attributes)
        );
    }

    /**
     * Trace component rendering
     *
     * @param string $componentId Component identifier
     * @param array<string, mixed> $attributes Additional trace attributes
     * @return OperationHandle Telemetry operation handle
     */
    public function traceRender(string $componentId, array $attributes = []): OperationHandle
    {
        return $this->telemetry->startOperation(
            name: 'livecomponent.render',
            type: 'view',
            attributes: array_merge([
                'component.id' => $componentId,
                'operation.type' => 'render',
            ], $attributes)
        );
    }

    /**
     * Trace action handling
     *
     * @param string $componentId Component identifier
     * @param string $actionName Action method name
     * @param array<string, mixed> $attributes Additional trace attributes
     * @return OperationHandle Telemetry operation handle
     */
    public function traceHandle(string $componentId, string $actionName, array $attributes = []): OperationHandle
    {
        return $this->telemetry->startOperation(
            name: 'livecomponent.handle',
            type: 'custom',
            attributes: array_merge([
                'component.id' => $componentId,
                'component.action' => $actionName,
                'operation.type' => 'handle',
            ], $attributes)
        );
    }

    /**
     * Trace file upload processing
     *
     * @param string $componentId Component identifier
     * @param string $uploadId Upload session identifier
     * @param array<string, mixed> $attributes Additional trace attributes
     * @return OperationHandle Telemetry operation handle
     */
    public function traceUpload(string $componentId, string $uploadId, array $attributes = []): OperationHandle
    {
        return $this->telemetry->startOperation(
            name: 'livecomponent.upload',
            type: 'filesystem',
            attributes: array_merge([
                'component.id' => $componentId,
                'upload.session_id' => $uploadId,
                'operation.type' => 'upload',
            ], $attributes)
        );
    }

    /**
     * Record a component lifecycle event
     *
     * @param string $eventName Event name (e.g., 'mounted', 'updated', 'destroyed')
     * @param string $componentId Component identifier
     * @param array<string, mixed> $attributes Additional event attributes
     */
    public function recordEvent(string $eventName, string $componentId, array $attributes = []): void
    {
        $this->telemetry->recordEvent(
            name: "livecomponent.{$eventName}",
            attributes: array_merge([
                'component.id' => $componentId,
            ], $attributes)
        );
    }

    /**
     * Record a component metric
     *
     * @param string $metricName Metric name
     * @param float $value Metric value
     * @param string $unit Unit of measurement
     * @param array<string, mixed> $attributes Additional metric attributes
     */
    public function recordMetric(string $metricName, float $value, string $unit = '', array $attributes = []): void
    {
        $this->telemetry->recordMetric(
            name: "livecomponent.{$metricName}",
            value: $value,
            unit: $unit,
            attributes: $attributes
        );
    }

    /**
     * Create a trace span for component operations
     *
     * @param string $spanName Span name
     * @param string $componentId Component identifier
     * @param array<string, mixed> $tags Additional span tags
     * @return TraceSpan|null Trace span or null if tracing disabled
     */
    public function startSpan(string $spanName, string $componentId, array $tags = []): ?TraceSpan
    {
        return Tracer::startSpan(
            name: $spanName,
            operation: 'livecomponent',
            tags: array_merge([
                'component.id' => $componentId,
            ], $tags)
        );
    }

    /**
     * Finish a trace span
     *
     * @param TraceSpan $span Span to finish
     */
    public function finishSpan(TraceSpan $span): void
    {
        Tracer::finishSpan($span);
    }

    /**
     * Execute a callable within a traced operation
     *
     * @template T
     * @param string $operationName Operation name (resolve, render, handle, upload)
     * @param string $componentId Component identifier
     * @param callable(): T $callback Function to trace
     * @param array<string, mixed> $attributes Additional attributes
     * @return T Result of callback
     */
    public function trace(string $operationName, string $componentId, callable $callback, array $attributes = []): mixed
    {
        $operationType = match($operationName) {
            'resolve' => 'custom',
            'render' => 'view',
            'handle' => 'custom',
            'upload' => 'filesystem',
            default => 'custom'
        };

        return $this->telemetry->trace(
            name: "livecomponent.{$operationName}",
            type: $operationType,
            callback: $callback,
            attributes: array_merge([
                'component.id' => $componentId,
                'operation.type' => $operationName,
            ], $attributes)
        );
    }

    /**
     * Get the current operation stack for debugging
     *
     * @return string Operation stack as string
     */
    public function getOperationStack(): string
    {
        return $this->telemetry->getOperationStack();
    }

    /**
     * Check if telemetry is enabled
     *
     * @return bool True if telemetry enabled
     */
    public function isEnabled(): bool
    {
        return $this->telemetry->isEnabled();
    }
}
