<?php
declare(strict_types=1);

require_once __DIR__ . '/cors.php';

function cadastro_out(array $payload, int $status = 200): void
{
    http_response_code($status);
    header('Content-Type: application/json; charset=utf-8');
    echo json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
    exit;
}

function cadastro_read_json(): array
{
    $raw = file_get_contents('php://input') ?: '';
    $json = json_decode($raw, true);

    if (!is_array($json)) {
        cadastro_out([
            'ok' => false,
            'message' => 'JSON inválido',
            'raw' => mb_substr($raw, 0, 300),
        ], 400);
    }

    return $json;
}

function cadastro_quote_ident(string $ident): string
{
    if (!preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $ident)) {
        throw new RuntimeException('Identificador SQL inválido: ' . $ident);
    }

    return '"' . str_replace('"', '""', $ident) . '"';
}

function cadastro_find_project_root(): string
{
    $dir = __DIR__;

    for ($i = 0; $i < 12; $i++) {
        if (is_dir($dir . '/app') || is_file($dir . '/.env')) {
            return $dir;
        }

        $parent = dirname($dir);
        if ($parent === $dir) {
            break;
        }

        $dir = $parent;
    }

    return dirname(__DIR__, 5);
}

function cadastro_load_app_core(): void
{
    static $loaded = false;

    if ($loaded) {
        return;
    }

    $root = cadastro_find_project_root();
    $autoload = $root . '/app/Core/Autoload.php';

    if (!is_file($autoload)) {
        cadastro_out(['ok' => false, 'message' => 'Autoload da API nÃ£o encontrado'], 500);
    }

    require_once $autoload;
    $loaded = true;
}

function cadastro_request_headers(): array
{
    if (function_exists('getallheaders')) {
        $headers = getallheaders();
        if (is_array($headers)) {
            return array_change_key_case($headers, CASE_LOWER);
        }
    }

    $headers = [];
    foreach ($_SERVER as $key => $value) {
        if (!str_starts_with($key, 'HTTP_')) {
            continue;
        }
        $name = strtolower(str_replace('_', '-', substr($key, 5)));
        $headers[$name] = $value;
    }

    return $headers;
}

function cadastro_bearer_token(): ?string
{
    $headers = cadastro_request_headers();
    $auth = $headers['authorization'] ?? $_SERVER['HTTP_AUTHORIZATION'] ?? $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ?? null;

    if (is_string($auth) && preg_match('/Bearer\s+(.+)/i', $auth, $m)) {
        return trim($m[1]);
    }

    return null;
}

function cadastro_auth_payload(): ?array
{
    static $payload = false;

    if ($payload !== false) {
        return $payload;
    }

    $token = cadastro_bearer_token();
    if (!$token) {
        $payload = null;
        return null;
    }

    try {
        cadastro_load_app_core();
        $decoded = \App\Core\Jwt::decode($token);
        $payload = is_array($decoded) ? $decoded : null;
        return $payload;
    } catch (Throwable) {
        $payload = null;
        return null;
    }
}

function cadastro_require_auth(): array
{
    $payload = cadastro_auth_payload();

    if (!$payload || empty($payload['sub'])) {
        cadastro_out(['ok' => false, 'message' => 'NÃ£o autenticado'], 401);
    }

    return $payload;
}

function cadastro_header(string $name): ?string
{
    $headers = cadastro_request_headers();
    $value = $headers[strtolower($name)] ?? null;

    return is_string($value) && trim($value) !== '' ? trim($value) : null;
}

function cadastro_token_municipio_id(?array $payload = null): ?string
{
    $payload ??= cadastro_auth_payload();

    foreach (['municipality_id', 'municipio_id'] as $key) {
        $value = isset($payload[$key]) ? trim((string) $payload[$key]) : '';
        if ($value !== '') {
            return $value;
        }
    }

    return null;
}

function cadastro_load_env_file(string $root): array
{
    $env = [];
    $file = $root . '/.env';

    if (!is_file($file)) {
        return $env;
    }

    $lines = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: [];

    foreach ($lines as $line) {
        $line = trim($line);

        if ($line === '' || str_starts_with($line, '#')) {
            continue;
        }

        if (!str_contains($line, '=')) {
            continue;
        }

        [$key, $value] = explode('=', $line, 2);
        $key = trim($key);
        $value = trim($value);

        // Remove aspas simples/duplas no começo e final.
        $value = trim($value, "\"'");

        if ($key !== '') {
            $env[$key] = $value;
        }
    }

    return $env;
}

function cadastro_config(): array
{
    static $cfg = null;

    if (is_array($cfg)) {
        return $cfg;
    }

    $root = cadastro_find_project_root();
    $env = cadastro_load_env_file($root);

    $configFile = $root . '/app/config/banco_el_jequie.php';
    $phpCfg = [];

    if (is_file($configFile)) {
        $tmp = require $configFile;
        if (is_array($tmp)) {
            $phpCfg = $tmp;
        }
    }

    $schemaFromPhp = $phpCfg['schema'] ?? null;
    if (is_array($schemaFromPhp)) {
        $schemaFromPhp = $schemaFromPhp[0] ?? null;
    }

    $cfg = [
        'host' => $env['DB_HOST'] ?? $env['POSTGRES_HOST'] ?? $phpCfg['host'] ?? '127.0.0.1',
        'port' => $env['DB_PORT'] ?? $env['POSTGRES_PORT'] ?? $phpCfg['port'] ?? '5432',
        'name' => $env['DB_NAME'] ?? $env['POSTGRES_DB'] ?? $phpCfg['name'] ?? $phpCfg['database'] ?? '',
        'user' => $env['DB_USER'] ?? $env['POSTGRES_USER'] ?? $phpCfg['user'] ?? '',
        'pass' => $env['DB_PASS'] ?? $env['DB_PASSWORD'] ?? $env['POSTGRES_PASSWORD'] ?? $phpCfg['pass'] ?? $phpCfg['password'] ?? '',
        'schema' => $env['CADASTRO_DB_SCHEMA'] ?? $env['BI_IPTU_FAST_DEFAULT_SCHEMA'] ?? $schemaFromPhp ?? 'mun_jequie',
        'central_schema' => $env['DB_SCHEMA'] ?? 'gestao_sisprom',
        'postgis_schema' => $env['POSTGIS_SCHEMA'] ?? 'gestao_muni',
        'municipio_id' => $env['CADASTRO_MUNICIPIO_ID'] ?? $env['VITE_DEFAULT_MUNICIPIO_ID'] ?? '',
        'root' => $root,
    ];

    if (!$cfg['name'] || !$cfg['user']) {
        throw new RuntimeException(
            'Configuração de banco incompleta. Informe DB_HOST, DB_PORT, DB_NAME, DB_USER, DB_PASS e CADASTRO_DB_SCHEMA no backend-php/.env ou app/config/banco_el_jequie.php.'
        );
    }

    $cfg = cadastro_apply_auth_municipio_context($cfg);

    if (!preg_match('/^mun_[a-zA-Z0-9_]+$/', (string) $cfg['schema'])) {
        throw new RuntimeException('CADASTRO_DB_SCHEMA inválido. Exemplo correto: mun_jequie');
    }

    if (!preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', (string) $cfg['central_schema'])) {
        throw new RuntimeException('DB_SCHEMA invÃ¡lido. Exemplo correto: gestao_sisprom');
    }

    if (!preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', (string) $cfg['postgis_schema'])) {
        throw new RuntimeException('POSTGIS_SCHEMA inválido. Exemplo correto: gestao_muni');
    }

    return $cfg;
}

function cadastro_apply_auth_municipio_context(array $cfg): array
{
    $payload = cadastro_auth_payload();
    $tokenMunicipio = cadastro_token_municipio_id($payload);
    $headerMunicipio = cadastro_header('x-municipality-id') ?: cadastro_header('x-municipio-id');

    if ($tokenMunicipio && $headerMunicipio && $tokenMunicipio !== $headerMunicipio) {
        cadastro_out(['ok' => false, 'message' => 'MunicÃ­pio do token nÃ£o confere com a requisiÃ§Ã£o'], 403);
    }

    $municipioId = $tokenMunicipio ?: $headerMunicipio ?: (trim((string) ($cfg['municipio_id'] ?? '')) ?: null);
    if (!$municipioId) {
        return $cfg;
    }

    $row = cadastro_find_municipio($cfg, $municipioId);
    $schema = $row['schema_name'] ?? null;

    if (!$schema && !empty($row['slug'])) {
        $slug = preg_replace('/[^a-z0-9_]/', '_', strtolower((string) $row['slug']));
        $schema = $slug ? 'mun_' . $slug : null;
    }

    if ($schema && preg_match('/^mun_[a-zA-Z0-9_]+$/', (string) $schema)) {
        $cfg['schema'] = (string) $schema;
    }

    $cfg['municipio_id'] = $municipioId;

    return $cfg;
}

function cadastro_raw_pdo(array $cfg): PDO
{
    static $cache = [];

    $key = md5(json_encode([$cfg['host'], $cfg['port'], $cfg['name'], $cfg['user']]));
    if (isset($cache[$key]) && $cache[$key] instanceof PDO) {
        return $cache[$key];
    }

    $dsn = sprintf('pgsql:host=%s;port=%s;dbname=%s', $cfg['host'], $cfg['port'], $cfg['name']);
    $cache[$key] = new PDO($dsn, $cfg['user'], $cfg['pass'], [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        PDO::ATTR_EMULATE_PREPARES => false,
        PDO::ATTR_PERSISTENT => false,
    ]);

    return $cache[$key];
}

function cadastro_table_exists(PDO $pdo, string $schema, string $table): bool
{
    $st = $pdo->prepare('SELECT 1 FROM information_schema.tables WHERE table_schema = :schema AND table_name = :table LIMIT 1');
    $st->execute([':schema' => $schema, ':table' => $table]);

    return (bool) $st->fetchColumn();
}

function cadastro_table_columns(PDO $pdo, string $schema, string $table): array
{
    static $cache = [];
    $key = $schema . '.' . $table;
    if (isset($cache[$key])) {
        return $cache[$key];
    }

    $st = $pdo->prepare('SELECT column_name FROM information_schema.columns WHERE table_schema = :schema AND table_name = :table');
    $st->execute([':schema' => $schema, ':table' => $table]);

    return $cache[$key] = array_map(static fn($row) => (string) $row['column_name'], $st->fetchAll() ?: []);
}

function cadastro_find_municipio(array $cfg, string $municipioId): ?array
{
    try {
        $pdo = cadastro_raw_pdo($cfg);
        $schema = (string) ($cfg['central_schema'] ?? 'gestao_sisprom');
        if (!cadastro_table_exists($pdo, $schema, 'municipios')) {
            return null;
        }

        $columns = cadastro_table_columns($pdo, $schema, 'municipios');
        if (!in_array('id', $columns, true)) {
            return null;
        }

        $select = ['id'];
        foreach (['nome', 'slug', 'schema_name'] as $column) {
            if (in_array($column, $columns, true)) {
                $select[] = $column;
            }
        }

        $qSchema = cadastro_quote_ident($schema);
        $sql = 'SELECT ' . implode(', ', array_map('cadastro_quote_ident', $select)) . " FROM {$qSchema}.municipios WHERE id = :id LIMIT 1";
        $st = $pdo->prepare($sql);
        $st->execute([':id' => $municipioId]);
        $row = $st->fetch();

        return is_array($row) ? $row : null;
    } catch (Throwable) {
        return null;
    }
}

function cadastro_user_roles(?array $payload = null): array
{
    $payload ??= cadastro_require_auth();
    $userId = trim((string) ($payload['sub'] ?? ''));
    if ($userId === '') {
        return [];
    }

    $roles = [];
    if (isset($payload['roles']) && is_array($payload['roles'])) {
        $roles = array_values(array_filter(array_map('strval', $payload['roles'])));
    }
    if (!empty($payload['role'])) {
        $roles[] = (string) $payload['role'];
    }

    try {
        $cfg = cadastro_config();
        $pdo = cadastro_raw_pdo($cfg);
        $schema = (string) ($cfg['central_schema'] ?? 'gestao_sisprom');
        if (!cadastro_table_exists($pdo, $schema, 'user_roles')) {
            return array_values(array_unique($roles));
        }

        $columns = cadastro_table_columns($pdo, $schema, 'user_roles');
        if (!in_array('user_id', $columns, true) || !in_array('role', $columns, true)) {
            return array_values(array_unique($roles));
        }

        $qSchema = cadastro_quote_ident($schema);
        $where = ['user_id = :user_id'];
        $params = [':user_id' => $userId];
        if (in_array('municipio_id', $columns, true)) {
            $where[] = '(municipio_id = :municipio_id OR municipio_id IS NULL)';
            $params[':municipio_id'] = $cfg['municipio_id'] ?: cadastro_token_municipio_id($payload);
        }

        $sql = "SELECT role FROM {$qSchema}.user_roles WHERE " . implode(' AND ', $where);
        $st = $pdo->prepare($sql);
        $st->execute($params);
        foreach ($st->fetchAll() ?: [] as $row) {
            if (!empty($row['role'])) {
                $roles[] = (string) $row['role'];
            }
        }
    } catch (Throwable) {
        return array_values(array_unique($roles));
    }

    return array_values(array_unique($roles));
}

function cadastro_require_write_access(): array
{
    $payload = cadastro_require_auth();
    $roles = cadastro_user_roles($payload);
    $allowed = ['super_admin', 'admin_municipal', 'gestor', 'operador'];

    if (array_intersect($allowed, $roles)) {
        return $payload;
    }

    cadastro_out([
        'ok' => false,
        'message' => 'UsuÃ¡rio sem permissÃ£o para editar geometrias',
    ], 403);
}

function cadastro_pdo(): PDO
{
    static $pdo = null;

    if ($pdo instanceof PDO) {
        return $pdo;
    }

    $cfg = cadastro_config();

    $dsn = sprintf(
        'pgsql:host=%s;port=%s;dbname=%s',
        $cfg['host'],
        $cfg['port'],
        $cfg['name']
    );

    $pdo = new PDO($dsn, $cfg['user'], $cfg['pass'], [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        PDO::ATTR_EMULATE_PREPARES => false,
        PDO::ATTR_PERSISTENT => false,
    ]);

    $schema = cadastro_quote_ident((string) $cfg['schema']);
    $postgisSchema = cadastro_quote_ident((string) $cfg['postgis_schema']);

    // IMPORTANTE:
    // No seu banco online, a tabela spatial_ref_sys está em gestao_muni.spatial_ref_sys.
    // Por isso o search_path precisa incluir o schema municipal + gestao_muni + public.
    $pdo->exec("SET search_path TO {$schema}, {$postgisSchema}, public");

    return $pdo;
}

function cadastro_schema(): string
{
    return (string) cadastro_config()['schema'];
}

function cadastro_postgis_schema(): string
{
    return (string) cadastro_config()['postgis_schema'];
}

function cadastro_user(): string
{
    $payload = cadastro_auth_payload();
    foreach (['username', 'usuario', 'email', 'sub'] as $key) {
        $user = isset($payload[$key]) ? trim((string) $payload[$key]) : '';
        if ($user !== '') {
            return $user;
        }
    }

    if (session_status() !== PHP_SESSION_ACTIVE) {
        @session_start();
    }

    $candidates = [
        $_SESSION['template']['login'] ?? null,
        $_SESSION['template']['username'] ?? null,
        $_SESSION['login'] ?? null,
        $_SESSION['username'] ?? null,
        $_SERVER['PHP_AUTH_USER'] ?? null,
    ];

    foreach ($candidates as $user) {
        $user = is_string($user) ? trim($user) : '';
        if ($user !== '') {
            return $user;
        }
    }

    return 'sistema';
}

function cadastro_geom_expr(string $param = ':wkt'): string
{
    return "ST_Multi(ST_SetSRID(ST_GeomFromText({$param}), 4674))";
}

function cadastro_area_expr(string $geomSql): string
{
    // geom está em SIRGAS 2000 / EPSG:4674. Geography retorna área em m².
    return "ROUND(ST_Area(({$geomSql})::geography)::numeric, 2)";
}

function cadastro_normalize_nullable($value): ?string
{
    if ($value === null) {
        return null;
    }

    $value = trim((string) $value);

    return $value === '' ? null : $value;
}

function cadastro_uuid_v4(): string
{
    $data = random_bytes(16);
    $data[6] = chr((ord($data[6]) & 0x0f) | 0x40);
    $data[8] = chr((ord($data[8]) & 0x3f) | 0x80);

    return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
}
