<?php

declare(strict_types=1);

/**
 * Front controller: the only PHP entry point under the document root.
 * All dynamic voting routes are handled here; history and legal pages
 * are static files served directly by Apache.
 */

use HowAreYou\App;
use HowAreYou\Config;
use HowAreYou\CountryLookup;
use HowAreYou\CsvCountryLookup;
use HowAreYou\Db;
use HowAreYou\NullCountryLookup;
use HowAreYou\PhpFileCountryLookup;

require dirname(__DIR__) . '/src/bootstrap.php';

$root = dirname(__DIR__);

$httpMethod = strtoupper((string) ($_SERVER['REQUEST_METHOD'] ?? 'GET'));
// HEAD behaves like GET without a response body (static-file parity).
$emitBody = $httpMethod !== 'HEAD';
if ($httpMethod === 'HEAD') {
    $httpMethod = 'GET';
}

try {
    // No silent fallback to the example config: without config.php (or
    // HOWAREYOU_CONFIG) the application fails clearly instead of starting
    // with example credentials.
    $config = Config::load(Config::configPath($root, $_SERVER), $_SERVER);

    /** @var callable $connect */
    $connect = static fn() => Db::connect($config);

    $lookup = buildLookup($config);

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

    $response = App::handle(
        $httpMethod,
        $path,
        $_GET,
        $_POST,
        (string) ($_SERVER['REMOTE_ADDR'] ?? ''),
        $config,
        $connect,
        $lookup,
        time(),
        $root . '/templates',
        $root . '/translations',
    );
} catch (Throwable $e) {
    // §62: minimal public response; details go to the server log only.
    error_log('howareyou: unhandled error: ' . get_class($e) . ': ' . $e->getMessage());
    $response = [
        'status' => 500,
        'headers' => [
            'Content-Type' => 'text/html; charset=utf-8',
            'Cache-Control' => 'no-store',
        ],
        'body' => "<!DOCTYPE html>\n<html lang=\"en\">\n<head><meta charset=\"utf-8\">"
            . "<title>Error</title></head>\n<body><h1>Error</h1></body>\n</html>\n",
    ];
}

http_response_code($response['status']);
foreach ($response['headers'] as $name => $value) {
    header($name . ': ' . $value);
}
// The application never sets cookies (§84); assert it structurally.
if (function_exists('headers_list')) {
    foreach (headers_list() as $sent) {
        if (str_starts_with(strtolower($sent), 'set-cookie:')) {
            error_log('howareyou: unexpected Set-Cookie header');
        }
    }
}
if ($emitBody) {
    echo $response['body'];
}

/**
 * @param array<string, mixed> $config
 */
function buildLookup(array $config): CountryLookup
{
    $provider = (string) ($config['geoip_provider'] ?? 'csv');
    if ($provider === 'null') {
        return new NullCountryLookup();
    }
    if ($provider === 'php') {
        // Precompiled buckets from bin/import-geoip.php (see docs/geoip.md).
        return new PhpFileCountryLookup((string) ($config['geoip_cache'] ?? ''));
    }
    // Missing/unreadable CSV files simply resolve to ZZ (§24).
    return new CsvCountryLookup(
        (string) ($config['geoip_csv_v4'] ?? ''),
        (string) ($config['geoip_csv_v6'] ?? ''),
    );
}
