<?php

declare(strict_types=1);

namespace GalpaoImperial\Api;

use Throwable;

final class HttpException extends \RuntimeException
{
    private int $statusCode;
    private array $errors;

    public function __construct(
        int $statusCode,
        string $message,
        array $errors = []
    ) {
        $this->statusCode = $statusCode;
        $this->errors = $errors;
        parent::__construct($message);
    }

    public function getStatusCode(): int
    {
        return $this->statusCode;
    }

    public function getErrors(): array
    {
        return $this->errors;
    }
}

function loadEnv(string $basePath): void
{
    $envPath = $basePath . DIRECTORY_SEPARATOR . '.env';
    if (!is_file($envPath)) {
        return;
    }

    $lines = file($envPath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
    if ($lines === false) {
        return;
    }

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

        [$name, $value] = explode('=', $line, 2);
        $name = trim($name);
        $value = trim($value, " \t\n\r\0\x0B\"'");

        if ($name !== '' && getenv($name) === false) {
            putenv(sprintf('%s=%s', $name, $value));
            $_ENV[$name] = $value;
            $_SERVER[$name] = $value;
        }
    }
}

function startSessionIfNeeded(): void
{
    if (session_status() === PHP_SESSION_NONE) {
        $sessionDir = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . '.php-sessions';
        if (!is_dir($sessionDir)) {
            mkdir($sessionDir, 0777, true);
        }
        session_save_path($sessionDir);
        session_name('GALPAOIMPERIALSESSID');
        session_start([
            'cookie_httponly' => true,
            'cookie_samesite' => 'Lax',
            'use_strict_mode' => true,
        ]);
    }
}

function env(string $name, ?string $default = null): ?string
{
    $value = $_ENV[$name] ?? $_SERVER[$name] ?? getenv($name);
    if ($value === false || $value === null || $value === '') {
        return $default;
    }

    return (string) $value;
}

function jsonBody(): array
{
    $raw = file_get_contents('php://input');
    if ($raw === false || trim($raw) === '') {
        return [];
    }

    $decoded = json_decode($raw, true);
    if (!is_array($decoded)) {
        throw new HttpException(400, 'JSON inválido.');
    }

    return $decoded;
}

function jsonResponse(array $payload, int $status = 200): void
{
    $origin = $_SERVER['HTTP_ORIGIN'] ?? '*';
    http_response_code($status);
    header('Content-Type: application/json; charset=utf-8');
    header('Vary: Origin');
    header('Access-Control-Allow-Origin: ' . $origin);
    header('Access-Control-Allow-Credentials: true');
    header('Access-Control-Allow-Headers: Content-Type, Authorization, X-Webhook-Token, asaas-access-token, access-token, x-asaas-token, token');
    header('Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS');
    echo json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    exit;
}

function getRequestHeaderValue(array $names): ?string
{
    $headers = function_exists('getallheaders') ? getallheaders() : [];
    foreach ($names as $name) {
        foreach ($headers as $key => $value) {
            if (mb_strtolower((string) $key) === mb_strtolower($name)) {
                return trim((string) $value);
            }
        }
    }

    foreach ($names as $name) {
        $serverKey = 'HTTP_' . strtoupper(str_replace('-', '_', $name));
        if (!empty($_SERVER[$serverKey])) {
            return trim((string) $_SERVER[$serverKey]);
        }
    }

    return null;
}

function adminUsername(): string
{
    return env('ADMIN_USERNAME', 'WENDSON') ?? 'WENDSON';
}

function adminPassword(): string
{
    return env('ADMIN_PASSWORD', '9141621091416210') ?? '9141621091416210';
}

function paymentAdvancedAccessKey(): string
{
    return env('PAYMENT_ADVANCED_KEY', '91416210') ?? '91416210';
}

function adminIsAuthenticated(): bool
{
    startSessionIfNeeded();
    return !empty($_SESSION['admin_authenticated']);
}

function platformIsAuthenticated(): bool
{
    startSessionIfNeeded();
    return !empty($_SESSION['platform_authenticated']);
}

function performPlatformLogin(string $key): bool
{
    startSessionIfNeeded();

    if (!hash_equals(paymentAdvancedAccessKey(), trim($key))) {
        return false;
    }

    $_SESSION['platform_authenticated'] = true;
    $_SESSION['platform_username'] = 'plataforma';
    $_SESSION['platform_login_at'] = date('c');
    return true;
}

function performPlatformLogout(): void
{
    startSessionIfNeeded();
    unset($_SESSION['platform_authenticated'], $_SESSION['platform_username'], $_SESSION['platform_login_at']);
}

function performAdminLogin(string $username, string $password): bool
{
    startSessionIfNeeded();

    $normalizedUsername = mb_strtolower(trim($username));

    try {
        $pdo = Database::connection();
        $stmt = $pdo->prepare(
            'SELECT id, username, password_hash
             FROM admin_users
             WHERE LOWER(username) = :username AND is_active = 1
             ORDER BY id DESC'
        );
        $stmt->execute(['username' => $normalizedUsername]);
        $admins = $stmt->fetchAll();

        foreach ($admins as $admin) {
            if ($admin && password_verify($password, (string) $admin['password_hash'])) {
                $_SESSION['admin_authenticated'] = true;
                $_SESSION['admin_user_id'] = (int) $admin['id'];
                $_SESSION['admin_username'] = (string) $admin['username'];
                $_SESSION['admin_login_at'] = date('c');
                return true;
            }
        }
    } catch (Throwable) {
        // Fallback for environments where the database is not ready yet.
    }

    if ($username !== adminUsername() || $password !== adminPassword()) {
        return false;
    }

    $_SESSION['admin_authenticated'] = true;
    $_SESSION['admin_username'] = $username;
    $_SESSION['admin_login_at'] = date('c');
    return true;
}

function performAdminLogout(): void
{
    startSessionIfNeeded();
    $_SESSION = [];
    session_regenerate_id(true);
    session_destroy();
}

function requireAdmin(): void
{
    if (!adminIsAuthenticated()) {
        throw new HttpException(401, 'Acesso administrativo não autorizado.');
    }
}

function requirePlatform(): void
{
    if (!platformIsAuthenticated()) {
        throw new HttpException(401, 'Acesso da plataforma não autorizado.');
    }
}

function ensureMethod(string $expected, string $current): void
{
    if (strtoupper($current) !== strtoupper($expected)) {
        throw new HttpException(405, 'Método não permitido.');
    }
}

function corsPreflight(string $method): void
{
    if (strtoupper($method) === 'OPTIONS') {
        jsonResponse(['success' => true], 200);
    }
}

function required(array $data, array $fields): void
{
    $missing = [];

    foreach ($fields as $field) {
        if (!array_key_exists($field, $data) || $data[$field] === '' || $data[$field] === null) {
            $missing[] = $field;
        }
    }

    if ($missing !== []) {
        throw new HttpException(422, 'Campos obrigatórios ausentes.', ['missing' => $missing]);
    }
}

function normalizePhone(string $value): string
{
    return preg_replace('/\D+/', '', $value) ?? '';
}

function generateOrderNumber(): string
{
    return '#' . str_pad((string) random_int(1, 999999), 6, '0', STR_PAD_LEFT);
}

function generateTicketCode(): string
{
    return 'GI-' . strtoupper(bin2hex(random_bytes(4)));
}

function normalizeGiftCode(string $value): string
{
    $value = strtoupper(trim($value));
    $value = preg_replace('/[^A-Z0-9-]+/', '-', $value) ?? '';
    $value = preg_replace('/-+/', '-', $value) ?? '';
    return trim($value, '-');
}

function normalizeCpf(string $value): string
{
    return preg_replace('/\D+/', '', $value) ?? '';
}

function paymentStatusIsPaid(string $status): bool
{
    return in_array(strtoupper($status), ['PAID', 'RECEIVED', 'CONFIRMED', 'RECEIVED_IN_CASH'], true);
}

function mapAsaasPaymentStatus(?string $status): string
{
    $normalized = strtoupper((string) $status);
    if (in_array($normalized, ['RECEIVED', 'CONFIRMED', 'RECEIVED_IN_CASH'], true)) {
        return 'paid';
    }
    if ($normalized === 'REFUNDED') {
        return 'refunded';
    }
    if (in_array($normalized, ['DELETED', 'CANCELED', 'CANCELLED'], true)) {
        return 'cancelled';
    }
    return 'pending';
}

function slugify(string $value): string
{
    $value = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $value) ?: $value;
    $value = strtolower($value);
    $value = preg_replace('/[^a-z0-9]+/', '-', $value) ?? '';
    return trim($value, '-') ?: 'item';
}

function todayIso(): string
{
    return date('Y-m-d');
}

function nowInBrasilia(): \DateTimeImmutable
{
    return new \DateTimeImmutable('now', new \DateTimeZone('America/Sao_Paulo'));
}

function serverErrorResponse(Throwable $throwable): void
{
    error_log('[Galpão Imperial API] unhandled error ' . json_encode([
        'type' => get_class($throwable),
        'message' => $throwable->getMessage(),
        'file' => $throwable->getFile(),
        'line' => $throwable->getLine(),
        'method' => $_SERVER['REQUEST_METHOD'] ?? null,
        'path' => parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH),
    ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));

    jsonResponse([
        'success' => false,
        'message' => 'Erro interno no servidor.',
        'debug' => env('APP_DEBUG', 'false') === 'true'
            ? [
                'error' => $throwable->getMessage(),
                'file' => $throwable->getFile(),
                'line' => $throwable->getLine(),
            ]
            : null,
    ], 500);
}
