<?php
declare(strict_types=1);

namespace ROITheme\Shared\Infrastructure\Di;

use ROITheme\Shared\Domain\Contracts\ComponentRepositoryInterface;
use ROITheme\Shared\Domain\Contracts\ComponentDefaultsRepositoryInterface;
use ROITheme\Shared\Domain\Contracts\ValidationServiceInterface;
use ROITheme\Shared\Domain\Contracts\CacheServiceInterface;
use ROITheme\Shared\Domain\Contracts\CSSGeneratorInterface;
use ROITheme\Shared\Domain\Contracts\ComponentSettingsRepositoryInterface;
use ROITheme\Shared\Domain\Contracts\CriticalCSSCollectorInterface;
use ROITheme\Shared\Infrastructure\Persistence\WordPress\WordPressComponentRepository;
use ROITheme\Shared\Infrastructure\Persistence\WordPress\WordPressDefaultsRepository;
use ROITheme\Shared\Infrastructure\Persistence\WordPress\WordPressComponentSettingsRepository;
use ROITheme\Shared\Infrastructure\Services\WordPressValidationService;
use ROITheme\Shared\Infrastructure\Services\WordPressCacheService;
use ROITheme\Shared\Infrastructure\Services\SchemaSyncService;
use ROITheme\Shared\Infrastructure\Services\CleanupService;
use ROITheme\Shared\Infrastructure\Services\CSSGeneratorService;
use ROITheme\Shared\Infrastructure\Services\CriticalCSSCollector;
use ROITheme\Shared\Application\UseCases\GetComponentSettings\GetComponentSettingsUseCase;
use ROITheme\Shared\Application\UseCases\SaveComponentSettings\SaveComponentSettingsUseCase;
use ROITheme\Public\AdsensePlacement\Infrastructure\Ui\AdsensePlacementRenderer;
use ROITheme\Shared\Domain\Contracts\PageVisibilityRepositoryInterface;
use ROITheme\Shared\Domain\Contracts\PageTypeDetectorInterface;
use ROITheme\Shared\Infrastructure\Services\WordPressPageTypeDetector;
use ROITheme\Shared\Infrastructure\Persistence\WordPress\WordPressPageVisibilityRepository;
use ROITheme\Shared\Application\UseCases\EvaluatePageVisibility\EvaluatePageVisibilityUseCase;
use ROITheme\Shared\Infrastructure\Services\MigratePageVisibilityService;
// Exclusion System (Plan 99.11)
use ROITheme\Shared\Domain\Contracts\ExclusionRepositoryInterface;
use ROITheme\Shared\Domain\Contracts\PageContextProviderInterface;
use ROITheme\Shared\Domain\Contracts\ServerRequestProviderInterface;
use ROITheme\Shared\Infrastructure\Persistence\WordPress\WordPressExclusionRepository;
use ROITheme\Shared\Infrastructure\Services\WordPressPageContextProvider;
use ROITheme\Shared\Infrastructure\Services\WordPressServerRequestProvider;
use ROITheme\Shared\Application\UseCases\EvaluateExclusions\EvaluateExclusionsUseCase;
use ROITheme\Shared\Application\UseCases\EvaluateComponentVisibility\EvaluateComponentVisibilityUseCase;
// Wrapper Visibility System (Plan 99.15)
use ROITheme\Shared\Domain\Contracts\WrapperVisibilityCheckerInterface;
use ROITheme\Shared\Infrastructure\Persistence\WordPress\WordPressComponentVisibilityRepository;
use ROITheme\Shared\Application\UseCases\CheckWrapperVisibilityUseCase;
use ROITheme\Shared\Infrastructure\Wordpress\BodyClassHooksRegistrar;

/**
 * DIContainer - Contenedor de Inyección de Dependencias
 *
 * RESPONSABILIDAD: Crear y gestionar instancias de servicios
 *
 * PATRÓN: Service Locator + Lazy Initialization
 * - Lazy Initialization: Crear instancias solo cuando se necesitan
 * - Singleton Pattern: Una sola instancia por servicio
 * - Dependency Resolution: Resolver dependencias automáticamente
 *
 * USO:
 * ```php
 * $container = new DIContainer($wpdb, '/path/to/schemas');
 * $repository = $container->getComponentRepository();
 * $service = $container->getValidationService();
 * ```
 *
 * @package ROITheme\Shared\Infrastructure\DI
 */
final class DIContainer
{
    private array $instances = [];

    /**
     * Instancia singleton del contenedor
     * @var self|null
     */
    private static ?self $instance = null;

    /**
     * Obtiene la instancia singleton del contenedor
     *
     * NOTA: Se debe haber creado una instancia previamente en functions.php
     * El constructor registra automáticamente la instancia.
     *
     * @return self
     * @throws \RuntimeException Si no se ha inicializado el contenedor
     */
    public static function getInstance(): self
    {
        if (self::$instance === null) {
            throw new \RuntimeException(
                'DIContainer no ha sido inicializado. Asegúrate de que functions.php se haya ejecutado primero.'
            );
        }
        return self::$instance;
    }

    public function __construct(
        private \wpdb $wpdb,
        private string $schemasPath
    ) {
        // Registrar como instancia singleton
        self::$instance = $this;
    }

    /**
     * Obtener repositorio de componentes
     *
     * Lazy initialization: Crea la instancia solo en la primera llamada
     *
     * @return ComponentRepositoryInterface
     */
    public function getComponentRepository(): ComponentRepositoryInterface
    {
        if (!isset($this->instances['componentRepository'])) {
            $this->instances['componentRepository'] = new WordPressComponentRepository(
                $this->wpdb
            );
        }

        return $this->instances['componentRepository'];
    }

    /**
     * Obtener repositorio de defaults
     *
     * Lazy initialization: Crea la instancia solo en la primera llamada
     *
     * @return ComponentDefaultsRepositoryInterface
     */
    public function getDefaultsRepository(): ComponentDefaultsRepositoryInterface
    {
        if (!isset($this->instances['defaultsRepository'])) {
            $this->instances['defaultsRepository'] = new WordPressDefaultsRepository(
                $this->wpdb
            );
        }

        return $this->instances['defaultsRepository'];
    }

    /**
     * Obtener servicio de validación
     *
     * Lazy initialization: Crea la instancia solo en la primera llamada
     * Resuelve dependencia: getDefaultsRepository()
     *
     * @return ValidationServiceInterface
     */
    public function getValidationService(): ValidationServiceInterface
    {
        if (!isset($this->instances['validationService'])) {
            $this->instances['validationService'] = new WordPressValidationService(
                $this->getDefaultsRepository()
            );
        }

        return $this->instances['validationService'];
    }

    /**
     * Obtener servicio de cache
     *
     * Lazy initialization: Crea la instancia solo en la primera llamada
     *
     * @return CacheServiceInterface
     */
    public function getCacheService(): CacheServiceInterface
    {
        if (!isset($this->instances['cacheService'])) {
            $this->instances['cacheService'] = new WordPressCacheService(
                $this->wpdb
            );
        }

        return $this->instances['cacheService'];
    }

    /**
     * Obtener servicio de sincronización de schemas
     *
     * Lazy initialization: Crea la instancia solo en la primera llamada
     * Resuelve dependencia: getDefaultsRepository()
     *
     * @return SchemaSyncService
     */
    public function getSchemaSyncService(): SchemaSyncService
    {
        if (!isset($this->instances['schemaSyncService'])) {
            $this->instances['schemaSyncService'] = new SchemaSyncService(
                $this->getDefaultsRepository(),
                $this->schemasPath
            );
        }

        return $this->instances['schemaSyncService'];
    }

    /**
     * Obtener servicio de limpieza
     *
     * Lazy initialization: Crea la instancia solo en la primera llamada
     * Resuelve dependencias: getComponentRepository(), getDefaultsRepository()
     *
     * @return CleanupService
     */
    public function getCleanupService(): CleanupService
    {
        if (!isset($this->instances['cleanupService'])) {
            $this->instances['cleanupService'] = new CleanupService(
                $this->getComponentRepository(),
                $this->getDefaultsRepository()
            );
        }

        return $this->instances['cleanupService'];
    }

    /**
     * Obtener servicio de generación de CSS
     *
     * Lazy initialization: Crea la instancia solo en la primera llamada
     * Sin dependencias
     *
     * @return CSSGeneratorInterface
     */
    public function getCSSGeneratorService(): CSSGeneratorInterface
    {
        if (!isset($this->instances['cssGeneratorService'])) {
            $this->instances['cssGeneratorService'] = new CSSGeneratorService();
        }

        return $this->instances['cssGeneratorService'];
    }

    /**
     * Obtener repositorio de configuraciones de componentes
     *
     * Lazy initialization: Crea la instancia solo en la primera llamada
     *
     * @return ComponentSettingsRepositoryInterface
     */
    public function getComponentSettingsRepository(): ComponentSettingsRepositoryInterface
    {
        if (!isset($this->instances['componentSettingsRepository'])) {
            $this->instances['componentSettingsRepository'] = new WordPressComponentSettingsRepository(
                $this->wpdb
            );
        }

        return $this->instances['componentSettingsRepository'];
    }

    /**
     * Obtener caso de uso para obtener configuraciones de componentes
     *
     * Lazy initialization: Crea la instancia solo en la primera llamada
     * Resuelve dependencia: getComponentSettingsRepository()
     *
     * @return GetComponentSettingsUseCase
     */
    public function getGetComponentSettingsUseCase(): GetComponentSettingsUseCase
    {
        if (!isset($this->instances['getComponentSettingsUseCase'])) {
            $this->instances['getComponentSettingsUseCase'] = new GetComponentSettingsUseCase(
                $this->getComponentSettingsRepository()
            );
        }

        return $this->instances['getComponentSettingsUseCase'];
    }

    /**
     * Obtener caso de uso para guardar configuraciones de componentes
     *
     * Lazy initialization: Crea la instancia solo en la primera llamada
     * Resuelve dependencia: getComponentSettingsRepository()
     *
     * @return SaveComponentSettingsUseCase
     */
    public function getSaveComponentSettingsUseCase(): SaveComponentSettingsUseCase
    {
        if (!isset($this->instances['saveComponentSettingsUseCase'])) {
            $this->instances['saveComponentSettingsUseCase'] = new SaveComponentSettingsUseCase(
                $this->getComponentSettingsRepository()
            );
        }

        return $this->instances['saveComponentSettingsUseCase'];
    }

    /**
     * Obtener renderer de AdSense Placement
     *
     * Lazy initialization: Crea la instancia solo en la primera llamada
     * Resuelve dependencia: getCSSGeneratorService()
     *
     * @return AdsensePlacementRenderer
     */
    public function getAdsensePlacementRenderer(): AdsensePlacementRenderer
    {
        if (!isset($this->instances['adsensePlacementRenderer'])) {
            $this->instances['adsensePlacementRenderer'] = new AdsensePlacementRenderer(
                $this->getCSSGeneratorService()
            );
        }

        return $this->instances['adsensePlacementRenderer'];
    }

    /**
     * Obtener colector de CSS crítico
     *
     * Lazy initialization: Crea la instancia solo en la primera llamada
     * IMPORTANTE: Singleton - misma instancia para todos los Renderers
     *
     * @return CriticalCSSCollectorInterface
     */
    public function getCriticalCSSCollector(): CriticalCSSCollectorInterface
    {
        if (!isset($this->instances['criticalCSSCollector'])) {
            $this->instances['criticalCSSCollector'] = new CriticalCSSCollector();
        }

        return $this->instances['criticalCSSCollector'];
    }

    // ===============================
    // Page Visibility System
    // ===============================

    /**
     * Obtiene el repositorio de visibilidad de página
     *
     * IMPORTANTE: Inyecta $wpdb para consistencia con el resto del código
     * (WordPressComponentSettingsRepository también recibe $wpdb por constructor)
     */
    public function getPageVisibilityRepository(): PageVisibilityRepositoryInterface
    {
        if (!isset($this->instances['pageVisibilityRepository'])) {
            // Inyectar $wpdb siguiendo el patrón existente
            $this->instances['pageVisibilityRepository'] = new WordPressPageVisibilityRepository($this->wpdb);
        }
        return $this->instances['pageVisibilityRepository'];
    }

    /**
     * Obtiene el detector de tipo de página
     */
    public function getPageTypeDetector(): PageTypeDetectorInterface
    {
        if (!isset($this->instances['pageTypeDetector'])) {
            $this->instances['pageTypeDetector'] = new WordPressPageTypeDetector();
        }
        return $this->instances['pageTypeDetector'];
    }

    /**
     * Obtiene el caso de uso de evaluación de visibilidad
     */
    public function getEvaluatePageVisibilityUseCase(): EvaluatePageVisibilityUseCase
    {
        if (!isset($this->instances['evaluatePageVisibilityUseCase'])) {
            $this->instances['evaluatePageVisibilityUseCase'] = new EvaluatePageVisibilityUseCase(
                $this->getPageTypeDetector(),
                $this->getPageVisibilityRepository()
            );
        }
        return $this->instances['evaluatePageVisibilityUseCase'];
    }

    /**
     * Obtiene el servicio de migración de visibilidad
     */
    public function getMigratePageVisibilityService(): MigratePageVisibilityService
    {
        if (!isset($this->instances['migratePageVisibilityService'])) {
            $this->instances['migratePageVisibilityService'] = new MigratePageVisibilityService(
                $this->getPageVisibilityRepository()
            );
        }
        return $this->instances['migratePageVisibilityService'];
    }

    // ===============================
    // Exclusion System (Plan 99.11)
    // ===============================

    /**
     * Obtiene el proveedor de request HTTP
     *
     * Encapsula acceso a $_SERVER
     */
    public function getServerRequestProvider(): ServerRequestProviderInterface
    {
        if (!isset($this->instances['serverRequestProvider'])) {
            $this->instances['serverRequestProvider'] = new WordPressServerRequestProvider();
        }
        return $this->instances['serverRequestProvider'];
    }

    /**
     * Obtiene el repositorio de exclusiones
     */
    public function getExclusionRepository(): ExclusionRepositoryInterface
    {
        if (!isset($this->instances['exclusionRepository'])) {
            $this->instances['exclusionRepository'] = new WordPressExclusionRepository($this->wpdb);
        }
        return $this->instances['exclusionRepository'];
    }

    /**
     * Obtiene el proveedor de contexto de página
     */
    public function getPageContextProvider(): PageContextProviderInterface
    {
        if (!isset($this->instances['pageContextProvider'])) {
            $this->instances['pageContextProvider'] = new WordPressPageContextProvider(
                $this->getServerRequestProvider()
            );
        }
        return $this->instances['pageContextProvider'];
    }

    /**
     * Obtiene el caso de uso de evaluación de exclusiones
     */
    public function getEvaluateExclusionsUseCase(): EvaluateExclusionsUseCase
    {
        if (!isset($this->instances['evaluateExclusionsUseCase'])) {
            $this->instances['evaluateExclusionsUseCase'] = new EvaluateExclusionsUseCase(
                $this->getExclusionRepository(),
                $this->getPageContextProvider()
            );
        }
        return $this->instances['evaluateExclusionsUseCase'];
    }

    /**
     * Obtiene el caso de uso orquestador de visibilidad completa
     *
     * Combina visibilidad por tipo de página + exclusiones
     */
    public function getEvaluateComponentVisibilityUseCase(): EvaluateComponentVisibilityUseCase
    {
        if (!isset($this->instances['evaluateComponentVisibilityUseCase'])) {
            $this->instances['evaluateComponentVisibilityUseCase'] = new EvaluateComponentVisibilityUseCase(
                $this->getEvaluatePageVisibilityUseCase(),
                $this->getEvaluateExclusionsUseCase()
            );
        }
        return $this->instances['evaluateComponentVisibilityUseCase'];
    }

    // ===============================
    // Wrapper Visibility System (Plan 99.15)
    // ===============================

    /**
     * Obtiene el repositorio de visibilidad de wrappers
     *
     * Implementa WrapperVisibilityCheckerInterface
     */
    public function getWrapperVisibilityChecker(): WrapperVisibilityCheckerInterface
    {
        if (!isset($this->instances['wrapperVisibilityChecker'])) {
            $this->instances['wrapperVisibilityChecker'] = new WordPressComponentVisibilityRepository($this->wpdb);
        }
        return $this->instances['wrapperVisibilityChecker'];
    }

    /**
     * Obtiene el caso de uso para verificar visibilidad de wrappers
     *
     * Usado por WrapperVisibilityService para templates
     */
    public function getCheckWrapperVisibilityUseCase(): CheckWrapperVisibilityUseCase
    {
        if (!isset($this->instances['checkWrapperVisibilityUseCase'])) {
            $this->instances['checkWrapperVisibilityUseCase'] = new CheckWrapperVisibilityUseCase(
                $this->getWrapperVisibilityChecker()
            );
        }
        return $this->instances['checkWrapperVisibilityUseCase'];
    }

    /**
     * Obtiene el registrador de hooks para body_class
     *
     * CSS failsafe: Agrega clases cuando componentes están ocultos
     */
    public function getBodyClassHooksRegistrar(): BodyClassHooksRegistrar
    {
        if (!isset($this->instances['bodyClassHooksRegistrar'])) {
            $this->instances['bodyClassHooksRegistrar'] = new BodyClassHooksRegistrar();
        }
        return $this->instances['bodyClassHooksRegistrar'];
    }
}
