<?php

declare(strict_types=1);

use App\CiotAPI;

if (PHP_SAPI === 'cli-server') {
    $requestedFile = __DIR__ . parse_url((string)($_SERVER['REQUEST_URI'] ?? ''), PHP_URL_PATH);
    if (is_file($requestedFile)) {
        return false;
    }
}

require_once __DIR__ . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'CiotAPI.php';

$config = require __DIR__ . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'ciot.php';

header('Content-Type: application/json; charset=utf-8');
header('X-Content-Type-Options: nosniff');
header('Access-Control-Allow-Origin: ' . (string)$config['cors_allowed_origin']);
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type, Accept');
header('Access-Control-Max-Age: 86400');

try {
    $method = strtoupper((string)($_SERVER['REQUEST_METHOD'] ?? 'GET'));
    $path = normalizePath(parse_url((string)($_SERVER['REQUEST_URI'] ?? '/'), PHP_URL_PATH) ?: '/');

    if ($method === 'OPTIONS') {
        respondJson(204, []);
    }

    if (str_starts_with($path, '/mock-provider/api/v1/ciot/')) {
        handleInternalMockProvider($method, $path);
    }

    if ($method === 'POST' && $path === '/certificado') {
        handleCertificateUpload($config);
    }

    if ($method === 'GET' && $path === '/certificado') {
        respondJson(200, getCertificateStatus($config));
    }

    if ($method === 'GET' && $path === '/' && acceptsHtml()) {
        header('Location: /teste.html', true, 302);
        exit;
    }

    if ($method === 'GET' && in_array($path, ['/', '/status'], true)) {
        respondJson(200, [
            'sucesso' => true,
            'mensagem' => 'API CIOT em homologacao pronta para receber JSON via AJAX.',
            'ambiente' => $config['environment'],
            'rotas' => [
                'emitir' => 'POST /emitir-ciot',
                'encerrar' => 'POST /encerrar-ciot',
                'certificado_upload' => 'POST /certificado',
                'certificado_status' => 'GET /certificado',
                'pagina_teste' => '/teste.html',
            ],
        ]);
    }

    if ($method !== 'POST') {
        respondJson(405, standardError('Metodo nao permitido. Utilize POST.'));
    }

    $requestData = readJsonBody();
    $config = applyAutomaticClientCertificate($config, $requestData);
    $ciotApi = makeCiotApi($config);

    $response = match ($path) {
        '/', '/emitir-ciot', '/api/ciot/emitir' => $ciotApi->emitirCiot($requestData),
        '/encerrar-ciot', '/api/ciot/encerrar' => $ciotApi->encerrarCiot($requestData),
        default => null,
    };

    if ($response === null) {
        respondJson(404, standardError('Rota nao encontrada. Use /emitir-ciot ou /encerrar-ciot.'));
    }

    $httpStatus = $response['sucesso'] === true ? 200 : mapFailureToHttpStatus($response['http_status'] ?? null);
    respondJson($httpStatus, $response);
} catch (JsonException) {
    respondJson(400, standardError('JSON invalido no corpo da requisicao.'));
} catch (Throwable $exception) {
    error_log('[CIOT API] ' . $exception->getMessage());

    respondJson(500, standardError('Erro interno ao processar a requisicao CIOT.'));
}

function normalizePath(string $path): string
{
    $scriptBase = str_replace('\\', '/', dirname((string)($_SERVER['SCRIPT_NAME'] ?? '')));
    if ($scriptBase !== '/' && $scriptBase !== '.' && str_starts_with($path, $scriptBase . '/')) {
        $path = substr($path, strlen($scriptBase));
    }

    $path = '/' . trim($path, '/');

    return $path === '/' ? '/' : rtrim($path, '/');
}

function acceptsHtml(): bool
{
    return str_contains(strtolower((string)($_SERVER['HTTP_ACCEPT'] ?? '')), 'text/html');
}

function handleInternalMockProvider(string $method, string $path): void
{
    if ($method !== 'POST') {
        respondJson(405, ['mensagem' => 'Metodo nao permitido.']);
    }

    $payload = readJsonBody();

    if ($path === '/mock-provider/api/v1/ciot/emissoes') {
        foreach (['motorista', 'veiculo', 'frete', 'origem', 'destino'] as $requiredKey) {
            if (!isset($payload[$requiredKey]) || !is_array($payload[$requiredKey])) {
                respondJson(400, ['mensagem' => 'Campo obrigatorio ausente: ' . $requiredKey]);
            }
        }

        $seed = hash('sha256', json_encode($payload, JSON_THROW_ON_ERROR));
        $ciot = substr(preg_replace('/\D+/', '', base_convert(substr($seed, 0, 12), 16, 10)), 0, 12);
        $protocol = 'HML-' . strtoupper(substr($seed, 12, 12));

        respondJson(201, [
            'mensagem' => 'CIOT homologacao Render emitido com sucesso.',
            'ciot' => str_pad($ciot, 12, '0', STR_PAD_LEFT),
            'protocolo' => $protocol,
            'data_emissao' => date(DATE_ATOM),
        ]);
    }

    if ($path === '/mock-provider/api/v1/ciot/encerramentos') {
        $ciot = preg_replace('/\D+/', '', (string)($payload['ciot'] ?? ''));
        if ($ciot === '') {
            respondJson(400, ['mensagem' => 'Numero do CIOT e obrigatorio para encerramento.']);
        }

        respondJson(200, [
            'mensagem' => 'CIOT homologacao Render encerrado com sucesso.',
            'ciot' => $ciot,
            'protocolo' => $payload['protocolo'] ?? 'ENC-HML-' . strtoupper(substr(hash('sha256', json_encode($payload, JSON_THROW_ON_ERROR)), 0, 10)),
            'data_encerramento' => $payload['data_encerramento'] ?? date(DATE_ATOM),
            'motivo' => $payload['motivo'] ?? 'ENTREGA_CONCLUIDA',
        ]);
    }

    respondJson(404, ['mensagem' => 'Endpoint mock nao encontrado.']);
}

/**
 * @return array<string, mixed>
 */
function readJsonBody(): array
{
    $rawBody = file_get_contents('php://input') ?: '';
    $requestData = json_decode($rawBody, true, 512, JSON_THROW_ON_ERROR);

    if (!is_array($requestData)) {
        respondJson(400, standardError('Corpo da requisicao deve ser um objeto JSON.'));
    }

    return $requestData;
}

/**
 * @param array<string, mixed> $config
 */
function makeCiotApi(array $config): CiotAPI
{
    return new CiotAPI([
        'environment' => $config['environment'],
        'provider_token' => $config['provider_token'],
        'homologation_url' => $config['homologation_url'],
        'production_url' => $config['production_url'],
        'timeout' => $config['timeout'],
        'connect_timeout' => $config['connect_timeout'],
        'log_file' => $config['log_file'],
        'client_certificate_path' => $config['client_certificate_path'],
        'client_certificate_password' => $config['client_certificate_password'],
        'client_certificate_type' => $config['client_certificate_type'],
    ]);
}

/**
 * @param array<string, mixed> $config
 * @param array<string, mixed> $requestData
 * @return array<string, mixed>
 */
function applyAutomaticClientCertificate(array $config, array $requestData): array
{
    if (!empty($config['client_certificate_path'])) {
        return $config;
    }

    $cnpj = findClientCnpj($requestData);
    if ($cnpj === null) {
        return $config;
    }

    $baseDir = (string)($config['certificate_storage_dir'] ?? dirname((string)$config['certificate_storage_path']));
    $metadataPath = $baseDir . DIRECTORY_SEPARATOR . $cnpj . DIRECTORY_SEPARATOR . 'certificado.json';
    if (!is_file($metadataPath)) {
        return $config;
    }

    $metadata = json_decode((string)file_get_contents($metadataPath), true);
    if (!is_array($metadata)) {
        return $config;
    }

    $config['client_certificate_path'] = $metadata['path'] ?? null;
    $config['client_certificate_password'] = $metadata['password'] ?? null;
    $config['client_certificate_type'] = $metadata['type'] ?? 'P12';
    $config['certificate_cnpj'] = $cnpj;

    return $config;
}

/**
 * @param array<string, mixed> $requestData
 */
function findClientCnpj(array $requestData): ?string
{
    foreach (['cliente_cnpj', 'cnpj_cliente', 'emitente_cnpj', 'cnpj'] as $key) {
        $cnpj = normalizeCnpj($requestData[$key] ?? null);
        if ($cnpj !== null) {
            return $cnpj;
        }
    }

    foreach ([['cliente', 'cnpj'], ['emitente', 'cnpj'], ['mdfe', 'emitente_cnpj'], ['cte', 'emitente_cnpj']] as $path) {
        $source = $requestData;
        foreach ($path as $key) {
            if (!is_array($source) || !array_key_exists($key, $source)) {
                continue 2;
            }

            $source = $source[$key];
        }

        $cnpj = normalizeCnpj($source);
        if ($cnpj !== null) {
            return $cnpj;
        }
    }

    return null;
}

function normalizeCnpj(mixed $value): ?string
{
    $cnpj = preg_replace('/\D+/', '', (string)($value ?? '')) ?? '';

    return strlen($cnpj) === 14 ? $cnpj : null;
}

/**
 * @param array<string, mixed> $config
 */
function handleCertificateUpload(array $config): void
{
    if (!isset($_FILES['certificado'])) {
        respondJson(400, standardError('Arquivo do certificado (.pfx) nao informado.'));
    }

    $password = trim((string)($_POST['senha'] ?? ''));
    if ($password === '') {
        respondJson(400, standardError('Senha do certificado nao informada.'));
    }

    $upload = $_FILES['certificado'];
    if (!is_array($upload) || ($upload['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) {
        respondJson(400, standardError('Falha ao enviar o arquivo do certificado.'));
    }

    $tmpName = (string)$upload['tmp_name'];
    $originalName = (string)$upload['name'];
    if (!str_ends_with(strtolower($originalName), '.pfx')) {
        respondJson(400, standardError('O certificado deve ser um arquivo .pfx.'));
    }

    $cnpj = normalizeCnpj($_POST['cliente_cnpj'] ?? $_POST['cnpj'] ?? null);
    $storagePath = (string)($config['certificate_storage_path'] ?? '');
    if ($cnpj !== null) {
        $baseDir = (string)($config['certificate_storage_dir'] ?? dirname($storagePath));
        $storagePath = $baseDir . DIRECTORY_SEPARATOR . $cnpj . DIRECTORY_SEPARATOR . 'certificado.json';
    }

    if ($storagePath === '') {
        respondJson(500, standardError('Caminho de armazenamento do certificado nao configurado.'));
    }

    $storageDir = dirname($storagePath);
    if (!is_dir($storageDir) && !mkdir($storageDir, 0750, true) && !is_dir($storageDir)) {
        respondJson(500, standardError('Nao foi possivel criar a pasta do certificado.'));
    }

    $destinationPfx = $storageDir . DIRECTORY_SEPARATOR . 'certificado.pfx';
    if (!move_uploaded_file($tmpName, $destinationPfx)) {
        respondJson(500, standardError('Nao foi possivel salvar o certificado no servidor.'));
    }

    $metadata = [
        'cnpj' => $cnpj,
        'path' => $destinationPfx,
        'password' => $password,
        'type' => 'P12',
        'uploaded_at' => date(DATE_ATOM),
    ];

    file_put_contents($storagePath, json_encode($metadata, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));

    respondJson(200, [
        'sucesso' => true,
        'mensagem' => 'Certificado enviado com sucesso.',
        'arquivo' => basename($destinationPfx),
        'data_upload' => $metadata['uploaded_at'],
    ]);
}

/**
 * @param array<string, mixed> $config
 * @return array<string, mixed>
 */
function getCertificateStatus(array $config): array
{
    $storagePath = (string)($config['certificate_storage_path'] ?? '');
    $cnpj = normalizeCnpj($_GET['cliente_cnpj'] ?? $_GET['cnpj'] ?? null);
    if ($cnpj !== null) {
        $baseDir = (string)($config['certificate_storage_dir'] ?? dirname($storagePath));
        $storagePath = $baseDir . DIRECTORY_SEPARATOR . $cnpj . DIRECTORY_SEPARATOR . 'certificado.json';
    }

    if ($storagePath === '' || !is_file($storagePath)) {
        return [
            'sucesso' => true,
            'configurado' => false,
            'mensagem' => 'Nenhum certificado enviado pelo site.',
        ];
    }

    $data = json_decode((string)file_get_contents($storagePath), true) ?: [];

    return [
        'sucesso' => true,
        'configurado' => true,
        'cnpj' => $data['cnpj'] ?? $cnpj,
        'arquivo' => isset($data['path']) ? basename((string)$data['path']) : null,
        'data_upload' => $data['uploaded_at'] ?? null,
    ];
}

function mapFailureToHttpStatus(?int $providerStatus): int
{
    return match ($providerStatus) {
        400, 401, 403, 404, 429, 500 => $providerStatus,
        default => 502,
    };
}

/**
 * @return array{sucesso: false, mensagem: string, ciot: null, protocolo: null, data_emissao: null}
 */
function standardError(string $message): array
{
    return [
        'sucesso' => false,
        'mensagem' => $message,
        'ciot' => null,
        'protocolo' => null,
        'data_emissao' => null,
    ];
}

/**
 * @param array<string, mixed> $payload
 */
function respondJson(int $statusCode, array $payload): void
{
    http_response_code($statusCode);

    if ($statusCode !== 204) {
        echo json_encode($payload, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
    }

    exit;
}
