<?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 normalizarRetirada(string $texto): string
{
    $texto = trim($texto);

    if (function_exists('mb_strtolower')) {
        $texto = mb_strtolower($texto, 'UTF-8');
    } else {
        $texto = strtolower($texto);
    }

    return strtr($texto, [
        'á' => 'a',
        'à' => 'a',
        'â' => 'a',
        'ã' => 'a',
        'é' => 'e',
        'è' => 'e',
        'ê' => 'e',
        'í' => 'i',
        'ó' => 'o',
        'ô' => 'o',
        'õ' => 'o',
        'ú' => 'u',
        'ç' => 'c'
    ]);
}

function codigoStatusRetirada(string $status): string
{
    $status = normalizarRetirada($status);

    if (str_contains($status, 'finaliz')) {
        return 'finalizado';
    }

    if (
        str_contains($status, 'pendente') &&
        str_contains($status, 'retirada')
    ) {
        return 'pendente_retirada';
    }

    return 'em_andamento';
}

function colunaExisteRetirada(
    mysqli $mysqli,
    string $tabela,
    string $coluna
): bool {
    $stmt = $mysqli->prepare("
        SELECT COUNT(*) AS total
        FROM INFORMATION_SCHEMA.COLUMNS
        WHERE TABLE_SCHEMA = DATABASE()
          AND TABLE_NAME = ?
          AND COLUMN_NAME = ?
    ");

    if (!$stmt) {
        return false;
    }

    $stmt->bind_param('ss', $tabela, $coluna);
    $stmt->execute();

    $linha = $stmt->get_result()->fetch_assoc();
    $stmt->close();

    return (int) ($linha['total'] ?? 0) > 0;
}

function garantirColunasRetirada(mysqli $mysqli): void
{
    if (
        !colunaExisteRetirada(
            $mysqli,
            'ordens_servico',
            'responsavel_retirada'
        )
    ) {
        if (!$mysqli->query("
            ALTER TABLE ordens_servico
            ADD COLUMN responsavel_retirada VARCHAR(150) NULL
            AFTER assinatura
        ")) {
            throw new RuntimeException(
                'Não foi possível criar o campo responsável pela retirada.'
            );
        }
    }

    if (
        !colunaExisteRetirada(
            $mysqli,
            'ordens_servico',
            'data_retirada'
        )
    ) {
        if (!$mysqli->query("
            ALTER TABLE ordens_servico
            ADD COLUMN data_retirada DATETIME NULL
            AFTER responsavel_retirada
        ")) {
            throw new RuntimeException(
                'Não foi possível criar o campo da data de retirada.'
            );
        }
    }
}

function voltarComErro(string $mensagem): never
{
    $_SESSION['retirada_flash'] = [
        'tipo' => 'erro',
        'mensagem' => $mensagem
    ];

    header('Location: listar_os.php?erro_retirada=1');
    exit;
}

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    header('Location: listar_os.php');
    exit;
}

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

$csrfRecebido = (string) ($_POST['csrf_token'] ?? '');
$csrfSessao = (string) ($_SESSION['csrf_retirada_os'] ?? '');

if (
    $csrfRecebido === '' ||
    $csrfSessao === '' ||
    !hash_equals($csrfSessao, $csrfRecebido)
) {
    voltarComErro(
        'A sessão do formulário expirou. Atualize a página e tente novamente.'
    );
}

if ($osId <= 0) {
    voltarComErro('A ordem de serviço informada é inválida.');
}

if (mb_strlen($responsavel, 'UTF-8') < 3) {
    voltarComErro(
        'Informe o nome completo do responsável pela retirada.'
    );
}

if (mb_strlen($responsavel, 'UTF-8') > 150) {
    voltarComErro(
        'O nome do responsável ultrapassa o limite permitido.'
    );
}

try {
    garantirColunasRetirada($mysqli);

    $mysqli->query("
        CREATE TABLE IF NOT EXISTS os_eventos (
            id INT AUTO_INCREMENT PRIMARY KEY,
            os_id INT NOT NULL,
            tipo VARCHAR(80) NOT NULL,
            descricao TEXT NOT NULL,
            usuario VARCHAR(150) DEFAULT NULL,
            criado_em DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
            INDEX idx_os_eventos_os_id (os_id)
        ) ENGINE=InnoDB
          DEFAULT CHARSET=utf8mb4
          COLLATE=utf8mb4_unicode_ci
    ");

    $mysqli->begin_transaction();

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

    if (!$stmt) {
        throw new RuntimeException(
            'Não foi possível consultar a ordem para retirada.'
        );
    }

    $stmt->bind_param('i', $osId);
    $stmt->execute();

    $os = $stmt->get_result()->fetch_assoc();
    $stmt->close();

    if (!$os) {
        throw new RuntimeException(
            'A ordem de serviço não foi encontrada.'
        );
    }

    $status = codigoStatusRetirada(
        (string) ($os['status'] ?? '')
    );

    if ($status === 'finalizado') {
        $mysqli->commit();

        header(
            'Location: comprovante_os.php?id=' .
            $osId .
            '&print=1'
        );
        exit;
    }

    if ($status !== 'pendente_retirada') {
        throw new RuntimeException(
            'Somente ordens pendentes de retirada podem ser entregues.'
        );
    }

    $dataRetirada = date('Y-m-d H:i:s');

    $mensagemPublica =
        'O equipamento foi entregue ao responsável e a ordem de serviço foi concluída.';

    $upd = $mysqli->prepare("
        UPDATE ordens_servico
        SET
            responsavel_retirada = ?,
            data_retirada = ?,
            status = 'Finalizado',
            mensagem_publica = ?
        WHERE id = ?
    ");

    if (!$upd) {
        throw new RuntimeException(
            'Não foi possível preparar a confirmação da retirada.'
        );
    }

    $upd->bind_param(
        'sssi',
        $responsavel,
        $dataRetirada,
        $mensagemPublica,
        $osId
    );

    if (!$upd->execute()) {
        $mensagem = $upd->error;
        $upd->close();

        throw new RuntimeException(
            'Erro ao confirmar a retirada: ' . $mensagem
        );
    }

    $upd->close();

    $usuarioSistema = trim(
        (string) ($_SESSION['usuario'] ?? 'Sistema')
    );

    $descricao = sprintf(
        'Equipamento retirado por %s. Termo de entrega disponibilizado para impressão e assinatura.',
        $responsavel
    );

    $evento = $mysqli->prepare("
        INSERT INTO os_eventos (
            os_id,
            tipo,
            descricao,
            usuario
        ) VALUES (?, 'Retirada', ?, ?)
    ");

    if (!$evento) {
        throw new RuntimeException(
            'Não foi possível registrar a retirada na linha do tempo.'
        );
    }

    $evento->bind_param(
        'iss',
        $osId,
        $descricao,
        $usuarioSistema
    );

    if (!$evento->execute()) {
        $mensagem = $evento->error;
        $evento->close();

        throw new RuntimeException(
            'Erro ao registrar o histórico: ' . $mensagem
        );
    }

    $evento->close();

    osPublicoGarantirToken($mysqli, $osId);

    osPublicoRegistrarAtualizacao(
        $mysqli,
        $osId,
        'Equipamento entregue',
        $mensagemPublica,
        'ENTREGUE',
        $usuarioSistema,
        1
    );

    $mysqli->commit();

    $_SESSION['csrf_retirada_os'] = bin2hex(random_bytes(24));

    $_SESSION['retirada_flash'] = [
        'tipo' => 'sucesso',
        'mensagem' => sprintf(
            'A OS %s foi finalizada e o termo de entrega foi gerado.',
            (string) ($os['numero_os'] ?? $osId)
        )
    ];

    header(
        'Location: comprovante_os.php?id=' .
        $osId .
        '&print=1'
    );
    exit;
} catch (Throwable $erro) {
    try {
        $mysqli->rollback();
    } catch (Throwable) {
        // A transação pode ainda não ter sido iniciada.
    }

    voltarComErro($erro->getMessage());
}
