<?php
// app/CApp.php

declare(strict_types=1);

use ScMedia\Http\Router;

final class CApp {
  private ?Router $router = null;
  private ?Container $container = null;

  public function run(): void {
    $this->loadEnv(__DIR__ . '/../.env');
    $this->registerErrorHandlers();

    require_once __DIR__ . '/http/helpers.php';
    require_once __DIR__ . '/http/Router.php';
    require_once __DIR__ . '/http/Response.php';
    require_once __DIR__ . '/Container.php';
    require_once __DIR__ . '/controllers/BaseController.php';

    $this->router = new Router();

    $method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
    $uri = $_SERVER['REQUEST_URI'] ?? '/';
    $path = parse_url($uri, PHP_URL_PATH) ?: '/';

    try {
      $services = require __DIR__ . '/bootstrap.php';
    } catch (Throwable $e) {
      $this->writeErrorResponse($e, $this->isDebugEnabled(), str_starts_with($path, '/api/'));
      exit;
    }

    $this->container = new Container($services);
    $this->registerControllers();

    $path = $this->handleLanguageRouting($path);
    $this->authorizeRequest($path);

    $this->router->dispatch($method, $path);
  }

  private function registerControllers(): void {
    require_once __DIR__ . '/controllers/pages.php';
    require_once __DIR__ . '/controllers/settings.php';
    require_once __DIR__ . '/controllers/scan_profiles.php';
    require_once __DIR__ . '/controllers/tools.php';
    require_once __DIR__ . '/controllers/layout.php';
    require_once __DIR__ . '/controllers/debug.php';
    require_once __DIR__ . '/controllers/media.php';
    require_once __DIR__ . '/controllers/logs.php';
    require_once __DIR__ . '/controllers/metadata.php';
    require_once __DIR__ . '/controllers/sources.php';
    require_once __DIR__ . '/controllers/events.php';
    require_once __DIR__ . '/controllers/tasks.php';
    require_once __DIR__ . '/controllers/auth.php';
    require_once __DIR__ . '/controllers/admin.php';
    require_once __DIR__ . '/controllers/account.php';
    require_once __DIR__ . '/controllers/items.php';
    require_once __DIR__ . '/controllers/jobs.php';

    $router = $this->router;
    $container = $this->container;
    PagesController::register($router, $container);
    SettingsController::register($router, $container);
    ScanProfilesController::register($router, $container);
    ToolsController::register($router, $container);
    LayoutController::register($router, $container);
    DebugController::register($router, $container);
    MediaController::register($router, $container);
    LogsController::register($router, $container);
    MetadataController::register($router, $container);
    SourcesController::register($router, $container);
    EventsController::register($router, $container);
    TasksController::register($router, $container);
    AuthController::register($router, $container);
    AdminController::register($router, $container);
    AccountController::register($router, $container);
    ItemsController::register($router, $container);
    JobsController::register($router, $container);
  }

  private function authorizeRequest(string $path): void {
    $services = $this->container->all();
    if (str_starts_with($path, '/api/')) {
      $publicApi = [
        '/api/auth/register',
        '/api/auth/login',
        '/api/auth/2fa/verify',
        '/api/auth/refresh',
        '/api/auth/logout',
        '/api/auth/password/forgot',
        '/api/auth/password/reset',
        '/api/language',
        '/api/events',
      ];
      if (!in_array($path, $publicApi, true)) {
        $requiredRole = str_starts_with($path, '/api/admin/') ? 'admin' : null;
        /** @var \ScMedia\Services\AuthService $auth */
        $auth = $services['auth'];
        $this->container->set('auth_user', $auth->requireAuth($requiredRole));
      }
      return;
    }

    header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
    header('Pragma: no-cache');
    header('Expires: 0');
    if ($path !== '/login') {
      /** @var \ScMedia\Services\AuthService $auth */
      $auth = $services['auth'];
      $this->container->set('auth_user', $auth->requirePageAuth());
    }
  }

  private function handleLanguageRouting(string $path): string {
    if (str_starts_with($path, '/api/')) {
      return $path;
    }

    $supportedLangs = ['en', 'ru', 'de'];
    if ($path === '/lang') {
      $lang = isset($_GET['lang']) ? strtolower((string)$_GET['lang']) : 'en';
      if (!in_array($lang, $supportedLangs, true)) {
        $lang = 'en';
      }
      $next = isset($_GET['next']) ? (string)$_GET['next'] : '/';
      if ($next === '' || $next[0] !== '/' || str_contains($next, '..')) {
        $next = '/';
      }
      $target = '/' . $lang . ($next === '/' ? '' : $next);
      setcookie('scmedia_lang', $lang, [
        'expires' => time() + 31536000,
        'path' => '/',
        'samesite' => 'Lax',
      ]);
      header('Location: ' . $target, true, 302);
      exit;
    }

    $first = explode('/', trim($path, '/'))[0] ?? '';
    $hasPrefix = in_array($first, $supportedLangs, true);
    if ($hasPrefix) {
      setcookie('scmedia_lang', $first, [
        'expires' => time() + 31536000,
        'path' => '/',
        'samesite' => 'Lax',
      ]);
      $_COOKIE['scmedia_lang'] = $first;
      $path = '/' . ltrim(substr($path, strlen($first) + 1), '/');
      if ($path === '/') {
        $path = '/';
      }
      return $path;
    }

    if (!str_starts_with($path, '/assets') && $path !== '/favicon.ico') {
      $lang = isset($_COOKIE['scmedia_lang']) ? (string)$_COOKIE['scmedia_lang'] : '';
      $token = (string)($_COOKIE['scmedia_access_token'] ?? '');
      if ($lang === '' && $token !== '' && isset($this->container->all()['auth'])) {
        /** @var \ScMedia\Services\AuthService $auth */
        $auth = $this->container->all()['auth'];
        $lang = $auth->resolveUserLanguageFromToken($token);
      }
      if ($lang === '' || $lang === null) {
        $lang = $this->pickLangFromHeader((string)($_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? ''), $supportedLangs);
      }
      if ($lang === '') $lang = 'en';
      $target = '/' . $lang . $path;
      header('Location: ' . $target, true, 302);
      exit;
    }

    return $path;
  }

  private function pickLangFromHeader(string $header, array $supported): string {
    $header = strtolower($header);
    $parts = array_map('trim', explode(',', $header));
    foreach ($parts as $part) {
      if ($part === '') continue;
      $lang = explode(';', $part)[0] ?? '';
      $lang = substr($lang, 0, 2);
      if (in_array($lang, $supported, true)) return $lang;
    }
    return 'en';
  }

  private function registerErrorHandlers(): void {
    set_exception_handler(function (Throwable $e): void {
      $uri = $_SERVER['REQUEST_URI'] ?? '/';
      $path = parse_url($uri, PHP_URL_PATH) ?: '/';
      $this->writeErrorResponse($e, $this->isDebugEnabled(), str_starts_with($path, '/api/'));
    });
    set_error_handler(function (int $severity, string $message, string $file, int $line): bool {
      throw new ErrorException($message, 0, $severity, $file, $line);
    });
    register_shutdown_function(function (): void {
      $error = error_get_last();
      if ($error === null) {
        return;
      }
      $fatal = [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR];
      if (!in_array($error['type'], $fatal, true)) {
        return;
      }
      $exception = new ErrorException($error['message'] ?? 'Fatal error', 0, $error['type'], $error['file'] ?? '', (int)($error['line'] ?? 0));
      $uri = $_SERVER['REQUEST_URI'] ?? '/';
      $path = parse_url($uri, PHP_URL_PATH) ?: '/';
      $this->writeErrorResponse($exception, $this->isDebugEnabled(), str_starts_with($path, '/api/'));
    });
  }

  private function isDebugEnabled(): bool {
    $val = getenv('DEBUG');
    if ($val === false) {
      return false;
    }
    $bool = filter_var($val, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
    return $bool ?? false;
  }

  private function writeErrorResponse(?Throwable $e, bool $debug, bool $isApi): void {
    static $sent = false;
    if ($sent) {
      return;
    }
    $sent = true;
    if (headers_sent()) {
      return;
    }
    $code = 500;
    $title = 'Internal Server Error';
    $message = 'Something went wrong. Please try again later.';
    if ($e instanceof PDOException) {
      $code = 503;
      $title = 'Database Unavailable';
      $message = 'Database is not available. Please try again later.';
    }
    @http_response_code($code);
    if ($isApi) {
      header('Content-Type: application/json; charset=utf-8');
      $payload = [
        'ok' => false,
        'error' => [
          'code' => 'SERVER_ERROR',
          'message' => $message,
        ],
      ];
      if ($debug && $e !== null) {
        $payload['error']['details'] = [
          'type' => get_class($e),
          'message' => $e->getMessage(),
          'trace' => $e->getTraceAsString(),
        ];
      }
      echo json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
      return;
    }
    header('Content-Type: text/html; charset=utf-8');
    $details = '';
    if ($debug && $e !== null) {
      $details = '<pre>' . htmlspecialchars($e->getMessage() . "\n\n" . $e->getTraceAsString(), ENT_QUOTES) . '</pre>';
    }
    echo '<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">';
    echo '<title>' . htmlspecialchars($title, ENT_QUOTES) . '</title></head><body style="font-family:Arial, sans-serif; padding:24px;">';
    echo '<h1>' . htmlspecialchars($title, ENT_QUOTES) . '</h1>';
    echo '<p>' . htmlspecialchars($message, ENT_QUOTES) . '</p>';
    echo $details;
    echo '</body></html>';
  }

  private function loadEnv(string $path): void {
    if (!is_file($path)) {
      return;
    }
    $lines = file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
    if (!is_array($lines)) {
      return;
    }
    foreach ($lines as $line) {
      $line = trim($line);
      if ($line === '' || str_starts_with($line, '#')) {
        continue;
      }
      $parts = explode('=', $line, 2);
      if (count($parts) !== 2) {
        continue;
      }
      $key = trim($parts[0]);
      if ($key === '' || getenv($key) !== false) {
        continue;
      }
      $value = trim($parts[1]);
      if ($value !== '' && ($value[0] === '"' || $value[0] === "'")) {
        $quote = $value[0];
        if (str_ends_with($value, $quote)) {
          $value = substr($value, 1, -1);
        }
      }
      putenv("{$key}={$value}");
      $_ENV[$key] = $value;
      $_SERVER[$key] = $value;
    }
  }
}
