<?php

declare(strict_types=1);

namespace Ekdos\Bootstrap;

use DI\ContainerBuilder;
use Ekdos\Auth\AuthService;
use Ekdos\Auth\SessionCookie;
use Ekdos\Auth\SessionStore;
use Ekdos\N8n\Cache;
use Ekdos\N8n\Client;
use Ekdos\N8n\Endpoints;
use Ekdos\Support\Config;
use Ekdos\Users\UserRepository;
use GuzzleHttp\Client as Guzzle;
use PDO;
use Psr\Container\ContainerInterface;
use Redis;

use function DI\autowire;
use function DI\factory;
use function DI\get;

/**
 * Der Kompositionswurzel. Alles, was einen Zustand oder eine Verbindung haelt,
 * wird hier genau einmal beschrieben.
 */
final class Container
{
    public static function build(string $projectRoot): ContainerInterface
    {
        $builder = new ContainerBuilder();
        $config = self::loadConfig($projectRoot);

        if (!$config->bool('APP_DEBUG', false)) {
            $builder->enableCompilation($projectRoot . '/var/cache');
        }

        $builder->addDefinitions([
            Config::class => $config,

            // ── PostgreSQL ────────────────────────────────────────────────
            PDO::class => factory(static function (Config $config): PDO {
                return new PDO(
                    $config->string('DB_DSN', 'pgsql:host=127.0.0.1;port=5432;dbname=ekdos'),
                    $config->string('DB_USER', 'ekdos'),
                    $config->string('DB_PASSWORD'),
                    [
                        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
                        PDO::ATTR_EMULATE_PREPARES => false,
                    ],
                );
            }),

            // ── Redis: Sitzungen und n8n-Cache ────────────────────────────
            Redis::class => factory(static function (Config $config): Redis {
                $redis = new Redis();
                $redis->connect($config->string('REDIS_HOST', '127.0.0.1'), $config->int('REDIS_PORT', 6379), 2.0);

                if ($config->has('REDIS_PASSWORD')) {
                    $redis->auth($config->string('REDIS_PASSWORD'));
                }

                $redis->select($config->int('REDIS_DB', 0));

                return $redis;
            }),

            SessionStore::class => factory(static function (Redis $redis, Config $config): SessionStore {
                return new SessionStore($redis, $config->string('REDIS_PREFIX', 'ekdos:'), $config->int('SESSION_TTL', 3600));
            }),

            SessionCookie::class => factory(static function (Config $config): SessionCookie {
                return new SessionCookie($config->string('SESSION_COOKIE', 'ekdos_session'), $config->bool('SESSION_COOKIE_SECURE', true));
            }),

            AuthService::class => factory(static function (UserRepository $users, SessionStore $sessions, Redis $redis, Config $config): AuthService {
                return new AuthService(
                    users: $users,
                    sessions: $sessions,
                    redis: $redis,
                    prefix: $config->string('REDIS_PREFIX', 'ekdos:'),
                    maxAttempts: $config->int('LOGIN_MAX_ATTEMPTS', 10),
                    window: $config->int('LOGIN_WINDOW', 900),
                );
            }),

            Cache::class => factory(static function (Redis $redis, Config $config): Cache {
                return new Cache($redis, $config->string('REDIS_PREFIX', 'ekdos:'));
            }),

            // ── n8n ───────────────────────────────────────────────────────
            Endpoints::class => factory(static fn (Config $config): Endpoints => Endpoints::fromConfig($config)),

            Guzzle::class => factory(static function (Config $config): Guzzle {
                return new Guzzle([
                    'timeout' => $config->int('N8N_TIMEOUT', 15),
                    'connect_timeout' => $config->int('N8N_CONNECT_TIMEOUT', 5),
                    // n8n antwortet gelegentlich mit einer Weiterleitung auf sich selbst.
                    'allow_redirects' => ['max' => 3],
                    'headers' => ['User-Agent' => 'EK-DOS-WEB/3.0'],
                ]);
            }),

            Client::class => autowire()->constructorParameter('http', get(Guzzle::class)),

            UserRepository::class => autowire(),
        ]);

        return $builder->build();
    }

    /** Liest .env, faellt aber auf echte Umgebungsvariablen zurueck (Container, systemd). */
    public static function loadConfig(string $projectRoot): Config
    {
        $values = getenv();

        if (is_readable($projectRoot . '/.env')) {
            $dotenv = \Dotenv\Dotenv::createArrayBacked($projectRoot);
            $values = $dotenv->load() + $values;
        }

        return Config::fromEnvironment($values);
    }
}
