<?php

declare(strict_types=1);

namespace App\Framework\ErrorReporting;

use App\Framework\Config\Environment;
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;
use App\Framework\ErrorReporting\ErrorReportingConfig;

/**
 * Initializer for Error Reporting system
 */
final readonly class ErrorReportingInitializer
{
    #[Initializer]
    public function initialize(Container $container): void
    {
        $env = $container->get(Environment::class);

        // Create configuration from environment
        $config = ErrorReportingConfig::fromEnvironment($env);

        // Register config as singleton
        $container->singleton(ErrorReportingConfig::class, $config);

        // Storage
        $container->bind(ErrorReportStorageInterface::class, function (Container $container) {
            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 ($config) {
            if (!$config->enabled) {
                return new NullErrorReporter();
            }

            return $this->createErrorReporter($container, $config);
        });

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

            return $this->createErrorReporter($container, $config);
        });

        // Middleware
        $container->bind(ErrorReportingMiddleware::class, function (Container $container) use ($config) {
            return new ErrorReportingMiddleware(
                reporter: $container->get(ErrorReporter::class),
                enabled: $config->enabled
            );
        });

        // 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);
        });
    }

    /**
     * Create ErrorReporter instance with processors and filters from config
     */
    private function createErrorReporter(Container $container, ErrorReportingConfig $config): ErrorReporter
    {
        $processors = [];
        $filters = [];

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

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

        // Add level-based filter from config
        if (!empty($config->filterLevels)) {
            $filters[] = function (ErrorReport $report) use ($config) {
                return $config->shouldReportLevel($report->level);
            };
        }

        // Add sampling filter
        if ($config->samplingRate < 100) {
            $filters[] = function (ErrorReport $report) use ($config) {
                return $config->shouldSample();
            };
        }

        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: $config->asyncProcessing,
            processors: $processors,
            filters: $filters
        );
    }
}
