<?php

declare(strict_types=1);

namespace App\Framework\LiveComponents\Performance;

use App\Framework\LiveComponents\ValueObjects\ComponentId;
use App\Framework\Performance\NestedPerformanceTracker;
use App\Framework\Performance\PerformanceCategory;

/**
 * Action Profiler for LiveComponent action execution analysis
 *
 * Provides detailed profiling for LiveComponent actions with:
 * - Parameter binding performance
 * - State validation performance
 * - Event dispatching performance
 * - Authorization check performance
 * - Rate limiting check performance
 */
final readonly class ActionProfiler
{
    public function __construct(
        private NestedPerformanceTracker $performanceTracker
    ) {}

    /**
     * Profile parameter binding operation
     *
     * Tracks how long it takes to bind parameters to action methods
     */
    public function profileParameterBinding(
        ComponentId $componentId,
        string $action,
        callable $bindingOperation
    ): mixed {
        return $this->performanceTracker->measure(
            "livecomponent.action.binding",
            PerformanceCategory::CUSTOM,
            $bindingOperation,
            [
                'component' => $componentId->name,
                'action' => $action
            ]
        );
    }

    /**
     * Profile authorization check
     *
     * Tracks authorization overhead for secured actions
     */
    public function profileAuthorizationCheck(
        ComponentId $componentId,
        string $action,
        callable $authCheck
    ): mixed {
        return $this->performanceTracker->measure(
            "livecomponent.action.authorization",
            PerformanceCategory::SECURITY,
            $authCheck,
            [
                'component' => $componentId->name,
                'action' => $action
            ]
        );
    }

    /**
     * Profile rate limiting check
     *
     * Tracks rate limiting overhead
     */
    public function profileRateLimitCheck(
        ComponentId $componentId,
        string $action,
        callable $rateLimitCheck
    ): mixed {
        return $this->performanceTracker->measure(
            "livecomponent.action.rateLimit",
            PerformanceCategory::SECURITY,
            $rateLimitCheck,
            [
                'component' => $componentId->name,
                'action' => $action
            ]
        );
    }

    /**
     * Profile CSRF validation
     *
     * Tracks CSRF token validation overhead
     */
    public function profileCsrfValidation(
        ComponentId $componentId,
        string $action,
        callable $csrfValidation
    ): mixed {
        return $this->performanceTracker->measure(
            "livecomponent.action.csrf",
            PerformanceCategory::SECURITY,
            $csrfValidation,
            [
                'component' => $componentId->name,
                'action' => $action
            ]
        );
    }

    /**
     * Profile event dispatching
     *
     * Tracks how long component events take to dispatch
     */
    public function profileEventDispatching(
        ComponentId $componentId,
        int $eventCount,
        callable $dispatchOperation
    ): mixed {
        return $this->performanceTracker->measure(
            "livecomponent.events.dispatch",
            PerformanceCategory::CUSTOM,
            $dispatchOperation,
            [
                'component' => $componentId->name,
                'event_count' => $eventCount
            ]
        );
    }

    /**
     * Profile idempotency check
     *
     * Tracks idempotency key validation overhead
     */
    public function profileIdempotencyCheck(
        ComponentId $componentId,
        string $action,
        callable $idempotencyCheck
    ): mixed {
        return $this->performanceTracker->measure(
            "livecomponent.action.idempotency",
            PerformanceCategory::CACHE,
            $idempotencyCheck,
            [
                'component' => $componentId->name,
                'action' => $action
            ]
        );
    }

    /**
     * Get profiling summary for specific component
     *
     * Analyzes all measured operations for a component
     */
    public function getComponentSummary(string $componentName): array
    {
        $timeline = $this->performanceTracker->generateTimeline();

        $componentOperations = array_filter(
            $timeline,
            fn($event) => isset($event['context']['component'])
                && $event['context']['component'] === $componentName
        );

        if (empty($componentOperations)) {
            return [
                'component' => $componentName,
                'total_operations' => 0,
                'total_time_ms' => 0,
                'operations' => []
            ];
        }

        $totalTime = array_sum(array_column($componentOperations, 'duration_ms'));
        $operationsByName = [];

        foreach ($componentOperations as $operation) {
            $name = $operation['name'];

            if (!isset($operationsByName[$name])) {
                $operationsByName[$name] = [
                    'count' => 0,
                    'total_time_ms' => 0,
                    'avg_time_ms' => 0,
                    'min_time_ms' => PHP_FLOAT_MAX,
                    'max_time_ms' => 0
                ];
            }

            $operationsByName[$name]['count']++;
            $operationsByName[$name]['total_time_ms'] += $operation['duration_ms'];
            $operationsByName[$name]['min_time_ms'] = min(
                $operationsByName[$name]['min_time_ms'],
                $operation['duration_ms']
            );
            $operationsByName[$name]['max_time_ms'] = max(
                $operationsByName[$name]['max_time_ms'],
                $operation['duration_ms']
            );
        }

        // Calculate averages
        foreach ($operationsByName as $name => &$stats) {
            $stats['avg_time_ms'] = $stats['total_time_ms'] / $stats['count'];
        }

        return [
            'component' => $componentName,
            'total_operations' => count($componentOperations),
            'total_time_ms' => $totalTime,
            'operations' => $operationsByName
        ];
    }

    /**
     * Get action execution metrics
     *
     * Provides detailed metrics for a specific action
     */
    public function getActionMetrics(string $componentName, string $actionName): array
    {
        $timeline = $this->performanceTracker->generateTimeline();

        $actionOperations = array_filter(
            $timeline,
            fn($event) => isset($event['context']['component'])
                && $event['context']['component'] === $componentName
                && isset($event['context']['action'])
                && $event['context']['action'] === $actionName
        );

        if (empty($actionOperations)) {
            return [
                'component' => $componentName,
                'action' => $actionName,
                'executions' => 0,
                'metrics' => []
            ];
        }

        $executionCount = 0;
        $totalTime = 0;
        $phaseTimings = [];

        foreach ($actionOperations as $operation) {
            // Count main action executions
            if (str_contains($operation['name'], "livecomponent.{$componentName}.{$actionName}")) {
                $executionCount++;
                $totalTime += $operation['duration_ms'];
            }

            // Collect phase timings
            $phase = $this->extractPhase($operation['name']);
            if ($phase) {
                if (!isset($phaseTimings[$phase])) {
                    $phaseTimings[$phase] = [
                        'count' => 0,
                        'total_ms' => 0,
                        'avg_ms' => 0
                    ];
                }

                $phaseTimings[$phase]['count']++;
                $phaseTimings[$phase]['total_ms'] += $operation['duration_ms'];
            }
        }

        // Calculate averages
        foreach ($phaseTimings as $phase => &$timing) {
            $timing['avg_ms'] = $timing['total_ms'] / $timing['count'];
        }

        return [
            'component' => $componentName,
            'action' => $actionName,
            'executions' => $executionCount,
            'total_time_ms' => $totalTime,
            'avg_time_per_execution_ms' => $executionCount > 0 ? $totalTime / $executionCount : 0,
            'phase_timings' => $phaseTimings
        ];
    }

    /**
     * Extract phase name from operation name
     */
    private function extractPhase(string $operationName): ?string
    {
        $phases = [
            'csrf' => 'CSRF Validation',
            'authorization' => 'Authorization',
            'rateLimit' => 'Rate Limiting',
            'idempotency' => 'Idempotency',
            'binding' => 'Parameter Binding',
            'schema.derive' => 'Schema Derivation',
            'action.execute' => 'Action Execution',
            'state.validate' => 'State Validation',
            'lifecycle.onUpdate' => 'Lifecycle Hook',
            'events.dispatch' => 'Event Dispatching'
        ];

        foreach ($phases as $key => $phaseName) {
            if (str_contains($operationName, $key)) {
                return $phaseName;
            }
        }

        return null;
    }

    /**
     * Generate performance report for all components
     *
     * Useful for system-wide performance analysis
     */
    public function generatePerformanceReport(): array
    {
        $timeline = $this->performanceTracker->generateTimeline();

        $componentOperations = array_filter(
            $timeline,
            fn($event) => str_starts_with($event['name'], 'livecomponent.')
                && isset($event['context']['component'])
        );

        $components = [];

        foreach ($componentOperations as $operation) {
            $componentName = $operation['context']['component'];

            if (!isset($components[$componentName])) {
                $components[$componentName] = [
                    'operations' => 0,
                    'total_time_ms' => 0,
                    'actions' => [],
                    'renders' => 0,
                    'cache_hits' => 0,
                    'cache_misses' => 0
                ];
            }

            $components[$componentName]['operations']++;
            $components[$componentName]['total_time_ms'] += $operation['duration_ms'];

            // Track actions
            if (isset($operation['context']['action'])) {
                $actionName = $operation['context']['action'];

                if (!isset($components[$componentName]['actions'][$actionName])) {
                    $components[$componentName]['actions'][$actionName] = 0;
                }

                $components[$componentName]['actions'][$actionName]++;
            }

            // Track renders
            if (str_contains($operation['name'], '.render.')) {
                $components[$componentName]['renders']++;
            }

            // Track cache hits/misses
            if (str_contains($operation['name'], 'cache.get')) {
                // Would need actual cache result to determine hit/miss
                // This is a placeholder for the pattern
            }
        }

        return [
            'total_components' => count($components),
            'total_operations' => count($componentOperations),
            'components' => $components,
            'generated_at' => date('Y-m-d H:i:s')
        ];
    }
}
