<?php

declare(strict_types=1);

namespace App\Framework\ErrorReporting;

use App\Framework\Database\ConnectionInterface;
use App\Framework\DateTime\Clock;
use App\Framework\DI\Container;
use App\Framework\DI\Initializer;
use App\Framework\ErrorReporting\Analytics\ErrorAnalyticsEngine;
use App\Framework\ErrorReporting\Processors\RequestContextProcessor;
use App\Framework\ErrorReporting\Processors\UserContextProcessor;
use App\Framework\ErrorReporting\Storage\DatabaseErrorReportStorage;
use App\Framework\ErrorReporting\Storage\ErrorReportStorageInterface;
use App\Framework\Http\Session\Session;
use App\Framework\Logging\Logger;
use App\Framework\Queue\Queue;

/**
 * Initializer for Error Reporting system
 */
final readonly class ErrorReportingInitializer
{
    #[Initializer]
    public function initialize(Container $container): void
    {
        $enabled = (bool) ($_ENV['ERROR_REPORTING_ENABLED'] ?? true);

        // Storage
        $container->bind(ErrorReportStorageInterface::class, function (Container $container) use ($enabled) {
            if (! $enabled) {
                // Return storage even if disabled (might be used for queries)
                return new DatabaseErrorReportStorage(
                    connection: $container->get(ConnectionInterface::class)
                );
            }

            return new DatabaseErrorReportStorage(
                connection: $container->get(ConnectionInterface::class)
            );
        });

        // Analytics Engine
        $container->bind(ErrorAnalyticsEngine::class, function (Container $container) {
            return new ErrorAnalyticsEngine(
                storage: $container->get(ErrorReportStorageInterface::class),
                clock: $container->get(Clock::class)
            );
        });

        // Error Reporter Interface - bind to concrete or Null implementation
        $container->bind(ErrorReporterInterface::class, function (Container $container) use ($enabled) {
            if (! $enabled) {
                return new NullErrorReporter();
            }

            $processors = [];
            $filters = [];

            // Add built-in processors
            if ($container->has(RequestContextProcessor::class)) {
                $processors[] = $container->get(RequestContextProcessor::class);
            }

            if ($container->has(UserContextProcessor::class)) {
                $processors[] = $container->get(UserContextProcessor::class);
            }

            // Add environment-based filters
            if (($_ENV['ERROR_REPORTING_FILTER_LEVELS'] ?? null)) {
                $allowedLevels = explode(',', $_ENV['ERROR_REPORTING_FILTER_LEVELS']);
                $filters[] = function (ErrorReport $report) use ($allowedLevels) {
                    return in_array($report->level, $allowedLevels);
                };
            }

            // Add environment filter for production
            if (($_ENV['APP_ENV'] ?? 'production') === 'production') {
                $filters[] = function (ErrorReport $report) {
                    // Don't report debug/info in production
                    return ! in_array($report->level, ['debug', 'info']);
                };
            }

            return new ErrorReporter(
                storage: $container->get(ErrorReportStorageInterface::class),
                clock: $container->get(Clock::class),
                logger: $container->has(Logger::class) ? $container->get(Logger::class) : null,
                queue: $container->has(Queue::class) ? $container->get(Queue::class) : null,
                asyncProcessing: (bool) ($_ENV['ERROR_REPORTING_ASYNC'] ?? true),
                processors: $processors,
                filters: $filters
            );
        });

        // Error Reporter (concrete class) - delegate to interface
        $container->bind(ErrorReporter::class, function (Container $container) use ($enabled) {
            if (! $enabled) {
                throw new \RuntimeException('ErrorReporter is disabled. Use ErrorReporterInterface instead.');
            }

            $processors = [];
            $filters = [];

            // Add built-in processors
            if ($container->has(RequestContextProcessor::class)) {
                $processors[] = $container->get(RequestContextProcessor::class);
            }

            if ($container->has(UserContextProcessor::class)) {
                $processors[] = $container->get(UserContextProcessor::class);
            }

            // Add environment-based filters
            if (($_ENV['ERROR_REPORTING_FILTER_LEVELS'] ?? null)) {
                $allowedLevels = explode(',', $_ENV['ERROR_REPORTING_FILTER_LEVELS']);
                $filters[] = function (ErrorReport $report) use ($allowedLevels) {
                    return in_array($report->level, $allowedLevels);
                };
            }

            // Add environment filter for production
            if (($_ENV['APP_ENV'] ?? 'production') === 'production') {
                $filters[] = function (ErrorReport $report) {
                    // Don't report debug/info in production
                    return ! in_array($report->level, ['debug', 'info']);
                };
            }

            return new ErrorReporter(
                storage: $container->get(ErrorReportStorageInterface::class),
                clock: $container->get(Clock::class),
                logger: $container->has(Logger::class) ? $container->get(Logger::class) : null,
                queue: $container->has(Queue::class) ? $container->get(Queue::class) : null,
                asyncProcessing: (bool) ($_ENV['ERROR_REPORTING_ASYNC'] ?? true),
                processors: $processors,
                filters: $filters
            );
        });

        // Middleware
        $container->bind(ErrorReportingMiddleware::class, function (Container $container) {
            return new ErrorReportingMiddleware(
                reporter: $container->get(ErrorReporter::class),
                enabled: (bool) ($_ENV['ERROR_REPORTING_ENABLED'] ?? true)
            );
        });

        // Processors
        $container->bind(RequestContextProcessor::class, function (Container $container) {
            return new RequestContextProcessor();
        });

        $container->bind(UserContextProcessor::class, function (Container $container) {
            // Session is not a singleton service - it's created per-request by SessionManager
            // Don't try to resolve it here as it may not exist or trigger circular dependencies
            return new UserContextProcessor(null);
        });
    }
}
