<?php

declare(strict_types=1);

namespace App\Framework\Console\Analytics\Middleware;

use App\Framework\Console\Analytics\Repository\CommandUsageRepository;
use App\Framework\Console\Analytics\ValueObjects\CommandUsageMetric;
use App\Framework\Console\ExitCode;
use App\Framework\Console\Input\ConsoleInput;
use App\Framework\Console\Middleware\ConsoleMiddleware;
use App\Framework\Console\Output\ConsoleOutput;
use App\Framework\Core\ValueObjects\Duration;
use DateTimeImmutable;

final readonly class AnalyticsCollectionMiddleware implements ConsoleMiddleware
{
    public function __construct(
        private CommandUsageRepository $repository,
        private bool $enabled = true
    ) {
    }

    public function handle(ConsoleInput $input, ConsoleOutput $output, callable $next): ExitCode
    {
        if (! $this->enabled) {
            return $next($input, $output);
        }

        $commandName = $this->extractCommandName($input);
        $startTime = hrtime(true);
        $executedAt = new DateTimeImmutable();

        try {
            $exitCode = $next($input, $output);
        } catch (\Throwable $e) {
            // Record the failure and re-throw
            $this->recordUsage(
                $commandName,
                $executedAt,
                $startTime,
                ExitCode::GENERAL_ERROR,
                $input
            );

            throw $e;
        }

        $this->recordUsage($commandName, $executedAt, $startTime, $exitCode, $input);

        return $exitCode;
    }

    private function recordUsage(
        string $commandName,
        DateTimeImmutable $executedAt,
        int $startTime,
        ExitCode $exitCode,
        ConsoleInput $input
    ): void {
        try {
            $endTime = hrtime(true);
            $executionTimeNs = $endTime - $startTime;
            $executionTime = Duration::fromNanoseconds($executionTimeNs);

            $metric = CommandUsageMetric::create(
                commandName: $commandName,
                executionTime: $executionTime,
                exitCode: $exitCode,
                argumentCount: count($input->getArguments()),
                userId: $this->getCurrentUserId(),
                metadata: $this->collectMetadata($input, $exitCode)
            );

            $this->repository->store($metric);
        } catch (\Throwable $e) {
            // Analytics collection should never break the command execution
            // In production, this could be logged to a separate error log
            error_log("Analytics collection failed: " . $e->getMessage());
        }
    }

    private function extractCommandName(ConsoleInput $input): string
    {
        $arguments = $input->getArguments();

        // First argument after script name is usually the command
        return $arguments[1] ?? 'unknown';
    }

    private function getCurrentUserId(): ?string
    {
        // In a real implementation, this would extract user ID from context
        // Could be from environment variables, session data, etc.
        return $_ENV['CONSOLE_USER_ID'] ?? null;
    }

    private function collectMetadata(ConsoleInput $input, ExitCode $exitCode): array
    {
        $metadata = [
            'options' => $input->getOptions(),
            'has_stdin' => ! empty(stream_get_contents(STDIN, 1)),
            'php_version' => PHP_VERSION,
            'memory_peak' => memory_get_peak_usage(true),
            'exit_code_name' => $exitCode->name,
        ];

        // Add environment context
        if (isset($_ENV['APP_ENV'])) {
            $metadata['environment'] = $_ENV['APP_ENV'];
        }

        // Add process info
        if (function_exists('posix_getpid')) {
            $metadata['process_id'] = posix_getpid();
        }

        return $metadata;
    }
}
