<?php
declare(strict_types=1);

require_once 'auth.php';
require_once 'conexao.php';
require_once 'os_publico_helpers.php';

date_default_timezone_set('America/Sao_Paulo');

if ($mysqli instanceof mysqli) {
    $mysqli->set_charset('utf8mb4');
}

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

function responder(array $dados, int $status = 200): never
{
    http_response_code($status);
    header('Content-Type: application/json; charset=utf-8');
    echo json_encode(
        $dados,
        JSON_UNESCAPED_UNICODE |
        JSON_UNESCAPED_SLASHES |
        JSON_HEX_TAG |
        JSON_HEX_APOS |
        JSON_HEX_QUOT |
        JSON_HEX_AMP
    );
    exit;
}

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    responder(['ok' => false, 'message' => 'Método não permitido.'], 405);
}

$csrf = (string) ($_POST['csrf_token'] ?? '');
$csrfSessao = (string) ($_SESSION['csrf_acompanhamento_os'] ?? '');

if ($csrf === '' || $csrfSessao === '' || !hash_equals($csrfSessao, $csrf)) {
    responder(['ok' => false, 'message' => 'A sessão expirou. Atualize a página.'], 419);
}

$acao = trim((string) ($_POST['acao'] ?? 'obter'));
$osId = filter_input(INPUT_POST, 'os_id', FILTER_VALIDATE_INT) ?: 0;

if ($osId <= 0) {
    responder(['ok' => false, 'message' => 'Ordem de serviço inválida.'], 422);
}

$stmt = $mysqli->prepare("
    SELECT
        id,
        numero_os,
        status,
        token_acompanhamento,
        acompanhamento_ativo,
        mensagem_publica
    FROM ordens_servico
    WHERE id = ?
    LIMIT 1
");

if (!$stmt) {
    responder(['ok' => false, 'message' => 'Não foi possível consultar a OS.'], 500);
}

$stmt->bind_param('i', $osId);
$stmt->execute();
$os = $stmt->get_result()->fetch_assoc();
$stmt->close();

if (!$os) {
    responder(['ok' => false, 'message' => 'Ordem de serviço não encontrada.'], 404);
}

try {
    if ($acao === 'obter') {
        $tokenOriginal = trim((string) ($os['token_acompanhamento'] ?? ''));
        $token = osPublicoGarantirToken($mysqli, $osId);
        $ativoAtual = $tokenOriginal === ''
            ? true
            : (int) ($os['acompanhamento_ativo'] ?? 1) === 1;

        $cienciaRegistrada = false;
        $avaliacaoNota = null;
        $avaliacaoResolvido = null;

        $stmtCiencia = $mysqli->prepare("
            SELECT id
            FROM os_atualizacoes_publicas
            WHERE os_id = ?
              AND titulo = 'Ciência do solicitante'
              AND visivel = 0
            LIMIT 1
        ");

        if ($stmtCiencia) {
            $stmtCiencia->bind_param('i', $osId);
            $stmtCiencia->execute();
            $cienciaRegistrada = (bool) $stmtCiencia
                ->get_result()
                ->fetch_assoc();
            $stmtCiencia->close();
        }

        $stmtAvaliacao = $mysqli->prepare("
            SELECT nota, resolvido
            FROM os_avaliacoes
            WHERE os_id = ?
            LIMIT 1
        ");

        if ($stmtAvaliacao) {
            $stmtAvaliacao->bind_param('i', $osId);
            $stmtAvaliacao->execute();
            $avaliacao = $stmtAvaliacao->get_result()->fetch_assoc();
            $stmtAvaliacao->close();

            if ($avaliacao) {
                $avaliacaoNota = (int) $avaliacao['nota'];
                $avaliacaoResolvido = (int) $avaliacao['resolvido'] === 1;
            }
        }

        responder([
            'ok' => true,
            'os_id' => $osId,
            'numero_os' => (string) $os['numero_os'],
            'status' => (string) $os['status'],
            'ativo' => $ativoAtual,
            'mensagem_publica' => (string) ($os['mensagem_publica'] ?? ''),
            'token' => $token,
            'url' => osPublicoUrl($token),
            'ciencia_registrada' => $cienciaRegistrada,
            'avaliacao_nota' => $avaliacaoNota,
            'avaliacao_resolvido' => $avaliacaoResolvido
        ]);
    }

    if ($acao === 'publicar') {
        $titulo = trim((string) ($_POST['titulo'] ?? ''));
        $mensagem = trim((string) ($_POST['mensagem'] ?? ''));
        $statusPublico = trim((string) ($_POST['status_publico'] ?? 'ATUALIZACAO'));
        $publicadoPor = trim((string) ($_SESSION['usuario'] ?? 'Equipe de TI'));

        $tamanhoTitulo = function_exists('mb_strlen')
            ? mb_strlen($titulo, 'UTF-8')
            : strlen($titulo);

        $tamanhoMensagem = function_exists('mb_strlen')
            ? mb_strlen($mensagem, 'UTF-8')
            : strlen($mensagem);

        if ($tamanhoTitulo < 3 || $tamanhoTitulo > 150) {
            responder(['ok' => false, 'message' => 'O título deve possuir entre 3 e 150 caracteres.'], 422);
        }

        if ($tamanhoMensagem > 500) {
            responder(['ok' => false, 'message' => 'A mensagem deve possuir até 500 caracteres.'], 422);
        }

        $permitidos = [
            'ATUALIZACAO',
            'EM_ANALISE',
            'AGUARDANDO_MATERIAL',
            'EM_MANUTENCAO',
            'TESTES',
            'PRONTO_RETIRADA',
            'ENTREGUE'
        ];

        if (!in_array($statusPublico, $permitidos, true)) {
            $statusPublico = 'ATUALIZACAO';
        }

        $mysqli->begin_transaction();

        osPublicoRegistrarAtualizacao(
            $mysqli,
            $osId,
            $titulo,
            $mensagem,
            $statusPublico,
            $publicadoPor,
            1
        );

        if ($mensagem !== '') {
            $upd = $mysqli->prepare("
                UPDATE ordens_servico
                SET mensagem_publica = ?
                WHERE id = ?
            ");

            if (!$upd) {
                throw new RuntimeException('Não foi possível atualizar a mensagem principal.');
            }

            $upd->bind_param('si', $mensagem, $osId);
            $upd->execute();
            $upd->close();
        }

        $token = osPublicoGarantirToken($mysqli, $osId);
        $mysqli->commit();

        responder([
            'ok' => true,
            'message' => 'Atualização publicada no painel do solicitante.',
            'url' => osPublicoUrl($token)
        ]);
    }

    if ($acao === 'alternar') {
        $ativo = (int) ($_POST['ativo'] ?? 0) === 1 ? 1 : 0;

        $upd = $mysqli->prepare("
            UPDATE ordens_servico
            SET acompanhamento_ativo = ?
            WHERE id = ?
        ");

        if (!$upd) {
            throw new RuntimeException('Não foi possível alterar o acompanhamento.');
        }

        $upd->bind_param('ii', $ativo, $osId);
        $upd->execute();
        $upd->close();

        responder([
            'ok' => true,
            'message' => $ativo
                ? 'Acompanhamento público ativado.'
                : 'Acompanhamento público desativado.',
            'ativo' => $ativo === 1
        ]);
    }

    if ($acao === 'regenerar') {
        $novoToken = '';

        for ($tentativa = 0; $tentativa < 5; $tentativa++) {
            $novoToken = osPublicoGerarToken();

            $upd = $mysqli->prepare("
                UPDATE ordens_servico
                SET
                    token_acompanhamento = ?,
                    acompanhamento_ativo = 1
                WHERE id = ?
            ");

            if (!$upd) {
                throw new RuntimeException('Não foi possível preparar o novo link.');
            }

            $upd->bind_param('si', $novoToken, $osId);

            if ($upd->execute()) {
                $upd->close();
                break;
            }

            $errno = $upd->errno;
            $upd->close();

            if ($errno !== 1062) {
                throw new RuntimeException('Não foi possível gerar um novo link.');
            }

            $novoToken = '';
        }

        if ($novoToken === '') {
            throw new RuntimeException('Não foi possível gerar um token exclusivo.');
        }

        responder([
            'ok' => true,
            'message' => 'Novo link gerado. O link anterior deixou de funcionar.',
            'token' => $novoToken,
            'url' => osPublicoUrl($novoToken),
            'ativo' => true
        ]);
    }

    responder(['ok' => false, 'message' => 'Ação não reconhecida.'], 400);
} catch (Throwable $erro) {
    try {
        $mysqli->rollback();
    } catch (Throwable) {
    }

    responder(['ok' => false, 'message' => $erro->getMessage()], 500);
}
