<?php

declare(strict_types=1);

namespace App\Framework\LiveComponents\Profiling;

use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Core\ValueObjects\Timestamp;
use App\Framework\LiveComponents\Profiling\ValueObjects\ProfilePhase;
use App\Framework\LiveComponents\Profiling\ValueObjects\ProfileResult;
use App\Framework\LiveComponents\Profiling\ValueObjects\MemorySnapshot;

/**
 * ProfileTimeline - Component Lifecycle Visualization
 *
 * Generates visualization-ready timeline data for:
 * - Chrome DevTools Performance Panel
 * - Custom visualization dashboards
 * - Performance analysis tools
 *
 * Timeline Event Types:
 * - Complete Events (X): Phases with duration (resolve, render, action)
 * - Instant Events (i): Memory snapshots, state changes
 * - Counter Events (C): Memory usage, object allocations
 * - Metadata Events (M): Component information, configuration
 */
final readonly class ProfileTimeline
{
    /**
     * Generate Chrome DevTools Performance Timeline
     *
     * Format: Trace Event Format (JSON)
     * Spec: https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU
     */
    public function generateDevToolsTimeline(ProfileResult $result): array
    {
        $events = [];

        // Add metadata event
        $events[] = $this->createMetadataEvent($result);

        // Add phase complete events
        $events = array_merge($events, $this->createPhaseEvents($result));

        // Add memory snapshot instant events
        $events = array_merge($events, $this->createMemorySnapshotEvents($result));

        // Add memory counter events
        $events = array_merge($events, $this->createMemoryCounterEvents($result));

        return $events;
    }

    /**
     * Generate simplified timeline for custom visualization
     */
    public function generateSimpleTimeline(ProfileResult $result): array
    {
        $timeline = [
            'component_id' => $result->componentId,
            'session_id' => $result->sessionId->toString(),
            'total_duration_ms' => $result->getTotalDurationMs(),
            'total_memory_mb' => $result->getTotalMemoryMB(),
            'start_time' => $result->startTime->toIso8601(),
            'end_time' => $result->endTime->toIso8601(),
            'phases' => [],
            'memory_snapshots' => [],
        ];

        $currentTime = 0.0;
        foreach ($result->phases as $phase) {
            $timeline['phases'][] = [
                'name' => $phase->name,
                'start_ms' => $currentTime,
                'end_ms' => $currentTime + $phase->getDurationMs(),
                'duration_ms' => $phase->getDurationMs(),
                'memory_mb' => $phase->getMemoryMB(),
                'success' => $phase->isSuccessful(),
                'error' => $phase->getError(),
            ];
            $currentTime += $phase->getDurationMs();
        }

        foreach ($result->memorySnapshots as $snapshot) {
            $timeline['memory_snapshots'][] = [
                'label' => $snapshot->label,
                'timestamp' => $snapshot->timestamp->toIso8601(),
                'current_mb' => $snapshot->getCurrentMemoryMB(),
                'peak_mb' => $snapshot->getPeakMemoryMB(),
                'allocated_objects' => $snapshot->allocatedObjects,
            ];
        }

        return $timeline;
    }

    /**
     * Generate Flamegraph-compatible data
     *
     * Format: function_name;parent_name count
     */
    public function generateFlamegraph(ProfileResult $result): string
    {
        $output = [];
        $baseStack = "livecomponent.{$result->componentId}";

        foreach ($result->phases as $phase) {
            $stack = "{$baseStack};{$phase->name}";
            $samples = (int) ceil($phase->getDurationMs());

            $output[] = "{$stack} {$samples}";

            // Add sub-stacks for phases with errors
            if (!$phase->isSuccessful()) {
                $errorStack = "{$stack};error";
                $output[] = "{$errorStack} {$samples}";
            }
        }

        return implode("\n", $output);
    }

    /**
     * Generate Gantt chart data for parallel execution visualization
     */
    public function generateGanttChart(ProfileResult $result): array
    {
        $tasks = [];
        $currentTime = $result->startTime->toFloat() * 1000; // Convert to milliseconds

        foreach ($result->phases as $index => $phase) {
            $startTime = $currentTime;
            $endTime = $currentTime + ($phase->getDurationMs() * 1000);

            $tasks[] = [
                'id' => "phase_{$index}",
                'name' => ucfirst($phase->name),
                'start' => $startTime,
                'end' => $endTime,
                'duration' => $phase->getDurationMs(),
                'memory' => $phase->getMemoryMB(),
                'success' => $phase->isSuccessful(),
                'type' => $this->getPhaseType($phase->name),
            ];

            $currentTime = $endTime;
        }

        return [
            'component_id' => $result->componentId,
            'total_duration_ms' => $result->getTotalDurationMs(),
            'tasks' => $tasks,
        ];
    }

    /**
     * Generate waterfall diagram data
     */
    public function generateWaterfall(ProfileResult $result): array
    {
        $entries = [];
        $currentTime = 0.0;

        foreach ($result->phases as $phase) {
            $entries[] = [
                'name' => $phase->name,
                'start_time' => $currentTime,
                'duration' => $phase->getDurationMs(),
                'memory_delta' => $phase->getMemoryMB(),
                'timing' => [
                    'blocked' => 0, // Could be extended with more granular timing
                    'execution' => $phase->getDurationMs(),
                    'total' => $phase->getDurationMs(),
                ],
                'status' => $phase->isSuccessful() ? 'success' : 'error',
            ];

            $currentTime += $phase->getDurationMs();
        }

        return [
            'component_id' => $result->componentId,
            'total_time' => $result->getTotalDurationMs(),
            'entries' => $entries,
        ];
    }

    /**
     * Create Chrome DevTools metadata event
     */
    private function createMetadataEvent(ProfileResult $result): array
    {
        return [
            'name' => 'thread_name',
            'ph' => 'M', // Metadata event
            'pid' => 1,
            'tid' => 1,
            'args' => [
                'name' => "LiveComponent: {$result->componentId}",
            ],
        ];
    }

    /**
     * Create Chrome DevTools complete events for phases
     */
    private function createPhaseEvents(ProfileResult $result): array
    {
        $events = [];
        $currentTime = $result->startTime->toFloat() * 1000000; // Microseconds

        foreach ($result->phases as $phase) {
            $events[] = [
                'name' => $phase->name,
                'cat' => 'livecomponent',
                'ph' => 'X', // Complete event
                'ts' => $currentTime,
                'dur' => $phase->getDurationMs() * 1000, // Microseconds
                'pid' => 1,
                'tid' => 1,
                'args' => [
                    'component_id' => $result->componentId,
                    'memory_delta_mb' => $phase->getMemoryMB(),
                    'success' => $phase->isSuccessful(),
                    ...$phase->attributes,
                ],
            ];

            $currentTime += $phase->getDurationMs() * 1000;
        }

        return $events;
    }

    /**
     * Create Chrome DevTools instant events for memory snapshots
     */
    private function createMemorySnapshotEvents(ProfileResult $result): array
    {
        $events = [];

        foreach ($result->memorySnapshots as $snapshot) {
            $events[] = [
                'name' => "memory.{$snapshot->label}",
                'cat' => 'livecomponent',
                'ph' => 'i', // Instant event
                'ts' => $snapshot->timestamp->toFloat() * 1000000,
                's' => 'g', // Global scope
                'pid' => 1,
                'tid' => 1,
                'args' => [
                    'current_mb' => $snapshot->getCurrentMemoryMB(),
                    'peak_mb' => $snapshot->getPeakMemoryMB(),
                    'allocated_objects' => $snapshot->allocatedObjects,
                ],
            ];
        }

        return $events;
    }

    /**
     * Create Chrome DevTools counter events for memory tracking
     */
    private function createMemoryCounterEvents(ProfileResult $result): array
    {
        $events = [];
        $currentMemory = 0.0;

        foreach ($result->phases as $phase) {
            $currentMemory += $phase->getMemoryMB();

            $events[] = [
                'name' => 'Memory Usage',
                'cat' => 'livecomponent',
                'ph' => 'C', // Counter event
                'ts' => $result->startTime->toFloat() * 1000000,
                'pid' => 1,
                'args' => [
                    'memory_mb' => $currentMemory,
                ],
            ];
        }

        return $events;
    }

    /**
     * Determine phase type for categorization
     */
    private function getPhaseType(string $phaseName): string
    {
        return match (true) {
            str_starts_with($phaseName, 'resolve') => 'initialization',
            str_starts_with($phaseName, 'render') => 'rendering',
            str_starts_with($phaseName, 'action') => 'interaction',
            str_starts_with($phaseName, 'cache') => 'optimization',
            default => 'other',
        };
    }

    /**
     * Export timeline as JSON
     */
    public function exportAsJson(ProfileResult $result, string $format = 'devtools'): string
    {
        $data = match ($format) {
            'devtools' => $this->generateDevToolsTimeline($result),
            'simple' => $this->generateSimpleTimeline($result),
            'gantt' => $this->generateGanttChart($result),
            'waterfall' => $this->generateWaterfall($result),
            default => throw new \InvalidArgumentException("Unknown timeline format: {$format}"),
        };

        return json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
    }
}
