<?php

namespace App\Core;

use App\Config\AuthServiceConfig;
use App\Domain\Repositories\TokenRepositoryInterface;
use App\Domain\Repositories\UserRepositoryInterface;
use App\Domain\Services\AuthService;
use App\Domain\Services\EmailVerificationService;
use App\Domain\Services\PasswordResetService;
use App\Domain\Services\RbacService;
use App\Domain\Services\TokenService;
use App\Drivers\CacheDriver;
use App\Drivers\DatabaseDriver;
use App\Exceptions\Handler;
use App\Infrastructure\Mail\Mailer;
use App\Infrastructure\Repositories\MySqlTokenRepository;
use App\Infrastructure\Repositories\MySqlUserRepository;
use App\Infrastructure\Security\JwtManager;
use App\Infrastructure\Security\PasswordHasher;
use App\Infrastructure\Security\RateLimiter;

class Application
{
    public Container $container;
    public Router $router;

    public function __construct(private string $basePath)
    {
        $this->loadEnv($basePath . '/.env');

        $this->container = new Container();
        $this->router = new Router();

        AuthServiceConfig::setBasePath($basePath);
        $this->registerServices();
        require $basePath . '/app/Http/Routes/api.php';
    }

    public function handle(Request $request): Response
    {
        try {
            return $this->router->dispatch($request, $this->container);
        } catch (\Throwable $e) {
            return Handler::render($e);
        }
    }

    private function registerServices(): void
    {
        $this->container->instance(self::class, $this);
        $this->container->instance(Container::class, $this->container);
        $this->container->instance(Router::class, $this->router);

        $this->container->singleton(DatabaseDriver::class, fn () => AuthServiceConfig::getDatabase());
        $this->container->singleton(CacheDriver::class, fn () => new CacheDriver(AuthServiceConfig::getConfig()['cache']));
        $this->container->singleton(PasswordHasher::class, fn () => new PasswordHasher());
        $this->container->singleton(JwtManager::class, fn () => new JwtManager(AuthServiceConfig::getConfig()['jwt']));
        $this->container->singleton(RateLimiter::class, fn ($c) => new RateLimiter($c->get(CacheDriver::class), AuthServiceConfig::getConfig()['security']));
        $this->container->singleton(Mailer::class, fn () => new Mailer(AuthServiceConfig::getConfig()['mail']));

        $this->container->singleton(UserRepositoryInterface::class, fn ($c) => new MySqlUserRepository($c->get(DatabaseDriver::class)));
        $this->container->singleton(TokenRepositoryInterface::class, fn ($c) => new MySqlTokenRepository($c->get(DatabaseDriver::class)));

        $this->container->singleton(TokenService::class, fn ($c) => new TokenService(
            $c->get(JwtManager::class),
            $c->get(TokenRepositoryInterface::class),
            $c->get(UserRepositoryInterface::class)
        ));
        $this->container->singleton(AuthService::class, fn ($c) => new AuthService(
            $c->get(UserRepositoryInterface::class),
            $c->get(PasswordHasher::class),
            $c->get(TokenService::class)
        ));
        $this->container->singleton(RbacService::class, fn () => new RbacService());
        $this->container->singleton(PasswordResetService::class, fn ($c) => new PasswordResetService(
            $c->get(UserRepositoryInterface::class),
            $c->get(PasswordHasher::class),
            $c->get(CacheDriver::class),
            $c->get(Mailer::class)
        ));
        $this->container->singleton(EmailVerificationService::class, fn ($c) => new EmailVerificationService(
            $c->get(UserRepositoryInterface::class),
            $c->get(CacheDriver::class),
            $c->get(Mailer::class)
        ));
    }

    private function loadEnv(string $path): void
    {
        if (!is_file($path)) {
            return;
        }

        foreach (file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) as $line) {
            $line = trim($line);
            if ($line === '' || str_starts_with($line, '#') || !str_contains($line, '=')) {
                continue;
            }

            [$key, $value] = explode('=', $line, 2);
            $key = trim($key);
            $value = trim($value, " \t\n\r\0\x0B\"'");
            $_ENV[$key] = $value;
            putenv($key . '=' . $value);
        }
    }
}
