<?php
declare(strict_types=1);

require_once __DIR__ . '/permissoes_ti.php';

function impE(mixed $valor): string
{
    return htmlspecialchars((string) $valor, ENT_QUOTES, 'UTF-8');
}

function impNormalizar(string $texto): string
{
    $texto = trim($texto);

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

    $texto = strtr($texto, [
        'á'=>'a','à'=>'a','â'=>'a','ã'=>'a','ä'=>'a',
        'é'=>'e','è'=>'e','ê'=>'e','ë'=>'e',
        'í'=>'i','ì'=>'i','î'=>'i','ï'=>'i',
        'ó'=>'o','ò'=>'o','ô'=>'o','õ'=>'o','ö'=>'o',
        'ú'=>'u','ù'=>'u','û'=>'u','ü'=>'u','ç'=>'c'
    ]);

    return preg_replace('/[^a-z0-9]+/', '', $texto) ?? '';
}

function impressorasUsuarioAtual(): string
{
    return trim((string) ($_SESSION['usuario'] ?? ''));
}

function impressorasAcessoPermitido(): bool
{
    global $mysqli;

    if (isset($mysqli) && $mysqli instanceof mysqli && tiPermEstruturaDisponivel($mysqli)) {
        return tiPermPode($mysqli, 'impressoras', 'visualizar');
    }

    return in_array(
        impNormalizar(impressorasUsuarioAtual()),
        ['renan', 'marcelo'],
        true
    );
}

function impressorasEhRenan(): bool
{
    return impNormalizar(impressorasUsuarioAtual()) === 'renan';
}

function impressorasExigirAcesso(): void
{
    if (impressorasAcessoPermitido()) {
        return;
    }

    http_response_code(403);

    echo '<!doctype html><html lang="pt-BR"><head><meta charset="utf-8">';
    echo '<meta name="viewport" content="width=device-width,initial-scale=1">';
    echo '<title>Acesso restrito</title>';
    echo '<style>body{margin:0;min-height:100vh;display:grid;place-items:center;';
    echo 'font-family:Arial,sans-serif;background:#edf3f8;color:#172438}';
    echo '.box{width:min(560px,calc(100% - 32px));padding:32px;border-radius:24px;';
    echo 'background:#fff;box-shadow:0 25px 70px rgba(8,57,111,.16);text-align:center}';
    echo 'h1{color:#bf263d}a{display:inline-flex;margin-top:15px;padding:12px 16px;';
    echo 'border-radius:12px;color:#fff;background:#08396f;text-decoration:none;font-weight:700}</style>';
    echo '</head><body><main class="box">';
    echo '<h1>Acesso restrito</h1>';
    echo '<p>O Controle de Impressoras está disponível somente para Marcelo e Renan.</p>';
    echo '<a href="listar_os.php">Voltar ao sistema</a>';
    echo '</main></body></html>';

    exit;
}

function impCsrf(): string
{
    if (empty($_SESSION['csrf_impressoras'])) {
        $_SESSION['csrf_impressoras'] = bin2hex(random_bytes(24));
    }

    return (string) $_SESSION['csrf_impressoras'];
}

function impValidarCsrf(): void
{
    $recebido = (string) ($_POST['csrf_token'] ?? '');
    $esperado = (string) ($_SESSION['csrf_impressoras'] ?? '');

    if (
        $recebido === '' ||
        $esperado === '' ||
        !hash_equals($esperado, $recebido)
    ) {
        throw new RuntimeException(
            'A sessão expirou. Atualize a página e tente novamente.'
        );
    }
}

function impTexto(mixed $valor, int $limite = 255): string
{
    $texto = trim((string) $valor);
    $texto = preg_replace('/\s+/u', ' ', $texto) ?? $texto;

    if (function_exists('mb_substr')) {
        return mb_substr($texto, 0, $limite, 'UTF-8');
    }

    return substr($texto, 0, $limite);
}

function impNulo(mixed $valor, int $limite = 255): ?string
{
    $texto = impTexto($valor, $limite);
    return $texto === '' ? null : $texto;
}


function impExecutar(
    mysqli $mysqli,
    string $sql,
    array $parametros = []
): mysqli_stmt {
    $stmt = $mysqli->prepare($sql);

    if (!$stmt) {
        throw new RuntimeException(
            'Não foi possível preparar a operação: ' . $mysqli->error
        );
    }

    if ($parametros !== []) {
        $tipos = '';
        $valores = [];

        foreach ($parametros as $valor) {
            if (is_int($valor) || is_bool($valor)) {
                $tipos .= 'i';
                $valores[] = (int) $valor;
            } elseif (is_float($valor)) {
                $tipos .= 'd';
                $valores[] = $valor;
            } else {
                $tipos .= 's';
                $valores[] = $valor;
            }
        }

        $referencias = [];

        foreach ($valores as $indice => $valor) {
            $referencias[$indice] = &$valores[$indice];
        }

        $stmt->bind_param($tipos, ...$referencias);
    }

    if (!$stmt->execute()) {
        $erro = $stmt->error;
        $stmt->close();

        throw new RuntimeException(
            'Não foi possível concluir a operação: ' . $erro
        );
    }

    return $stmt;
}

function impFlash(string $tipo, string $mensagem): void
{
    $_SESSION['impressoras_flash'] = [
        'tipo' => $tipo,
        'mensagem' => $mensagem
    ];
}

function impConsumirFlash(): ?array
{
    $flash = $_SESSION['impressoras_flash'] ?? null;
    unset($_SESSION['impressoras_flash']);

    return is_array($flash) ? $flash : null;
}

function impRedirecionar(string $url): never
{
    header('Location: ' . $url);
    exit;
}

function impCodigo(string $prefixo = 'IMP'): string
{
    return sprintf(
        '%s-%s-%s',
        strtoupper($prefixo),
        date('YmdHis'),
        strtoupper(substr(bin2hex(random_bytes(4)), 0, 8))
    );
}

function impNumeroAtendimento(): string
{
    return sprintf(
        'PRI-%s-%04d',
        date('Ymd'),
        random_int(1, 9999)
    );
}

function impNumeroTermo(string $tipo): string
{
    return sprintf(
        'TRI-%s-%s-%05d',
        strtoupper(substr($tipo, 0, 3)),
        date('Ymd'),
        random_int(1, 99999)
    );
}

function impTabelaExiste(mysqli $mysqli, string $tabela): bool
{
    $nome = $mysqli->real_escape_string($tabela);
    $resultado = $mysqli->query("SHOW TABLES LIKE '{$nome}'");

    return $resultado instanceof mysqli_result &&
        $resultado->num_rows > 0;
}

function impStatus(string $status): array
{
    return match (strtoupper(trim($status))) {
        'EM_USO' => ['rotulo'=>'Em uso no setor','classe'=>'st-use','icone'=>'bi-building-check'],
        'AGUARDANDO_DIAGNOSTICO' => ['rotulo'=>'Aguardando diagnóstico','classe'=>'st-warn','icone'=>'bi-search'],
        'EM_MANUTENCAO' => ['rotulo'=>'Em manutenção','classe'=>'st-maint','icone'=>'bi-tools'],
        'AGUARDANDO_PECA' => ['rotulo'=>'Aguardando peça','classe'=>'st-orange','icone'=>'bi-hourglass-split'],
        'AGUARDANDO_TONER' => ['rotulo'=>'Aguardando toner','classe'=>'st-orange','icone'=>'bi-printer-fill'],
        'PRONTA_RETIRADA' => ['rotulo'=>'Pronta para retirada','classe'=>'st-ready','icone'=>'bi-box-arrow-up-right'],
        'ESTOQUE_RESERVA' => ['rotulo'=>'Estoque / reserva','classe'=>'st-stock','icone'=>'bi-box-seam-fill'],
        'ASSISTENCIA_EXTERNA' => ['rotulo'=>'Assistência externa','classe'=>'st-purple','icone'=>'bi-truck'],
        'NAO_LOCALIZADA' => ['rotulo'=>'Não localizada','classe'=>'st-danger','icone'=>'bi-exclamation-octagon-fill'],
        'AGUARDANDO_BAIXA' => ['rotulo'=>'Aguardando baixa','classe'=>'st-muted','icone'=>'bi-archive'],
        'BAIXADA' => ['rotulo'=>'Baixada','classe'=>'st-muted','icone'=>'bi-archive-fill'],
        default => ['rotulo'=>'Cadastro inicial','classe'=>'st-info','icone'=>'bi-info-circle-fill']
    };
}

function impCondicao(string $condicao): string
{
    return match (strtoupper(trim($condicao))) {
        'NOVO' => 'Novo',
        'BOM' => 'Bom',
        'REGULAR' => 'Regular',
        'DANIFICADO' => 'Danificado',
        default => 'Não avaliado'
    };
}

function impData(?string $data): string
{
    if (!$data) {
        return 'Não registrada';
    }

    $timestamp = strtotime($data);

    return $timestamp === false
        ? $data
        : date('d/m/Y H:i', $timestamp);
}

function impRegistrarHistorico(
    mysqli $mysqli,
    int $impressoraId,
    ?int $manutencaoId,
    string $acao,
    ?string $statusAnterior,
    ?string $statusNovo,
    ?string $setorAnterior,
    ?string $setorNovo,
    ?string $localAnterior,
    ?string $localNovo,
    ?string $detalhes = null
): void {
    $usuario = impressorasUsuarioAtual() ?: 'Usuário autenticado';

    $stmt = $mysqli->prepare("
        INSERT INTO ti_impressora_movimentacoes (
            impressora_id,
            manutencao_id,
            acao,
            status_anterior,
            status_novo,
            setor_anterior,
            setor_novo,
            local_anterior,
            local_novo,
            detalhes,
            usuario
        )
        VALUES (?, NULLIF(?, 0), ?, ?, ?, ?, ?, ?, ?, ?, ?)
    ");

    if (!$stmt) {
        throw new RuntimeException(
            'Não foi possível preparar o histórico da impressora.'
        );
    }

    $manutencao = $manutencaoId ?? 0;

    $stmt->bind_param(
        'iisssssssss',
        $impressoraId,
        $manutencao,
        $acao,
        $statusAnterior,
        $statusNovo,
        $setorAnterior,
        $setorNovo,
        $localAnterior,
        $localNovo,
        $detalhes,
        $usuario
    );

    if (!$stmt->execute()) {
        $erro = $stmt->error;
        $stmt->close();

        throw new RuntimeException(
            'Não foi possível registrar o histórico: ' . $erro
        );
    }

    $stmt->close();
}

function impBuscarImpressora(
    mysqli $mysqli,
    int $id,
    bool $forUpdate = false
): ?array {
    $sql = "
        SELECT
            i.*,
            s.nome AS setor_nome,
            s.secretaria AS setor_secretaria,
            s.predio AS setor_predio,
            s.andar AS setor_andar,
            a.nome AS ambiente_nome
        FROM ti_impressoras AS i
        LEFT JOIN ti_setores AS s
            ON s.id = i.setor_id
        LEFT JOIN ti_ambientes AS a
            ON a.id = i.ambiente_id
        WHERE i.id = ?
        LIMIT 1
    ";

    if ($forUpdate) {
        $sql .= " FOR UPDATE";
    }

    $stmt = $mysqli->prepare($sql);

    if (!$stmt) {
        return null;
    }

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

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

    return $linha ?: null;
}

function impManutencaoAtiva(
    mysqli $mysqli,
    int $impressoraId,
    bool $forUpdate = false
): ?array {
    $sql = "
        SELECT *
        FROM ti_impressora_manutencoes
        WHERE impressora_id = ?
          AND status NOT IN ('RETIRADA', 'CANCELADA')
        ORDER BY id DESC
        LIMIT 1
    ";

    if ($forUpdate) {
        $sql .= " FOR UPDATE";
    }

    $stmt = $mysqli->prepare($sql);

    if (!$stmt) {
        return null;
    }

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

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

    return $linha ?: null;
}

function impCriarTermo(
    mysqli $mysqli,
    int $impressoraId,
    int $manutencaoId,
    string $tipo,
    string $pessoa,
    ?string $setor,
    string $tecnico,
    array $dados
): int {
    $numero = impNumeroTermo($tipo);
    $token = hash('sha256', random_bytes(32) . microtime(true));
    $json = json_encode(
        $dados,
        JSON_UNESCAPED_UNICODE |
        JSON_UNESCAPED_SLASHES |
        JSON_HEX_TAG |
        JSON_HEX_APOS |
        JSON_HEX_QUOT |
        JSON_HEX_AMP
    );

    $stmt = $mysqli->prepare("
        INSERT INTO ti_impressora_termos (
            impressora_id,
            manutencao_id,
            numero_termo,
            tipo,
            pessoa_nome,
            setor_nome,
            tecnico_nome,
            dados_json,
            token,
            status
        )
        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'GERADO')
    ");

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

    $stmt->bind_param(
        'iisssssss',
        $impressoraId,
        $manutencaoId,
        $numero,
        $tipo,
        $pessoa,
        $setor,
        $tecnico,
        $json,
        $token
    );

    if (!$stmt->execute()) {
        $erro = $stmt->error;
        $stmt->close();

        throw new RuntimeException(
            'Não foi possível gerar o termo: ' . $erro
        );
    }

    $id = (int) $stmt->insert_id;
    $stmt->close();

    return $id;
}

function impResolverSetor(
    mysqli $mysqli,
    int $setorId,
    ?string $novoNome,
    ?string $secretaria,
    ?string $predio,
    ?string $andar
): ?int {
    if ($setorId > 0) {
        return $setorId;
    }

    $nome = impNulo($novoNome, 150);

    if ($nome === null) {
        return null;
    }

    $usuario = impressorasUsuarioAtual() ?: 'Usuário autenticado';

    $stmt = $mysqli->prepare("
        SELECT id
        FROM ti_setores
        WHERE LOWER(TRIM(nome)) = LOWER(TRIM(?))
          AND COALESCE(predio, '') = COALESCE(?, '')
        LIMIT 1
    ");

    $stmt->bind_param('ss', $nome, $predio);
    $stmt->execute();

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

    if ($existente) {
        return (int) $existente['id'];
    }

    $stmt = $mysqli->prepare("
        INSERT INTO ti_setores (
            nome,
            secretaria,
            predio,
            andar,
            criado_por
        )
        VALUES (?, ?, ?, ?, ?)
    ");

    $stmt->bind_param(
        'sssss',
        $nome,
        $secretaria,
        $predio,
        $andar,
        $usuario
    );

    if (!$stmt->execute()) {
        $erro = $stmt->error;
        $stmt->close();

        throw new RuntimeException(
            'Não foi possível cadastrar o setor: ' . $erro
        );
    }

    $id = (int) $stmt->insert_id;
    $stmt->close();

    return $id;
}

function impResolverAmbiente(
    mysqli $mysqli,
    ?int $setorId,
    int $ambienteId,
    ?string $novoNome
): ?int {
    if ($ambienteId > 0) {
        return $ambienteId;
    }

    $nome = impNulo($novoNome, 150);

    if (!$setorId || $nome === null) {
        return null;
    }

    $usuario = impressorasUsuarioAtual() ?: 'Usuário autenticado';

    $stmt = $mysqli->prepare("
        SELECT id
        FROM ti_ambientes
        WHERE setor_id = ?
          AND LOWER(TRIM(nome)) = LOWER(TRIM(?))
        LIMIT 1
    ");

    $stmt->bind_param('is', $setorId, $nome);
    $stmt->execute();

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

    if ($existente) {
        return (int) $existente['id'];
    }

    $stmt = $mysqli->prepare("
        INSERT INTO ti_ambientes (
            setor_id,
            nome,
            tipo,
            grid_coluna,
            grid_linha,
            grid_largura,
            grid_altura,
            criado_por
        )
        VALUES (?, ?, 'SALA', 1, 1, 3, 1, ?)
    ");

    $stmt->bind_param('iss', $setorId, $nome, $usuario);

    if (!$stmt->execute()) {
        $erro = $stmt->error;
        $stmt->close();

        throw new RuntimeException(
            'Não foi possível cadastrar o ambiente: ' . $erro
        );
    }

    $id = (int) $stmt->insert_id;
    $stmt->close();

    return $id;
}

function impSalvarAnexos(
    mysqli $mysqli,
    int $impressoraId,
    int $manutencaoId,
    array $arquivos
): int {
    if (
        empty($arquivos['name']) ||
        !is_array($arquivos['name'])
    ) {
        return 0;
    }

    $permitidos = [
        'image/jpeg' => 'jpg',
        'image/png' => 'png',
        'application/pdf' => 'pdf'
    ];

    $base = __DIR__ . '/uploads/impressoras/' . date('Y/m');

    if (!is_dir($base) && !mkdir($base, 0775, true) && !is_dir($base)) {
        throw new RuntimeException(
            'Não foi possível criar a pasta de anexos.'
        );
    }

    $salvos = 0;
    $finfo = new finfo(FILEINFO_MIME_TYPE);
    $usuario = impressorasUsuarioAtual() ?: 'Usuário autenticado';

    foreach ($arquivos['name'] as $indice => $nomeOriginal) {
        $erroUpload = (int) ($arquivos['error'][$indice] ?? UPLOAD_ERR_NO_FILE);

        if ($erroUpload === UPLOAD_ERR_NO_FILE) {
            continue;
        }

        if ($erroUpload !== UPLOAD_ERR_OK) {
            throw new RuntimeException(
                'Um dos anexos não pôde ser enviado.'
            );
        }

        $temporario = (string) ($arquivos['tmp_name'][$indice] ?? '');
        $tamanho = (int) ($arquivos['size'][$indice] ?? 0);

        if ($tamanho <= 0 || $tamanho > 8 * 1024 * 1024) {
            throw new RuntimeException(
                'Cada anexo deve possuir no máximo 8 MB.'
            );
        }

        $mime = $finfo->file($temporario) ?: '';

        if (!isset($permitidos[$mime])) {
            throw new RuntimeException(
                'Anexos permitidos: PDF, JPG e PNG.'
            );
        }

        $extensao = $permitidos[$mime];
        $nomeSeguro = bin2hex(random_bytes(16)) . '.' . $extensao;
        $destino = $base . '/' . $nomeSeguro;

        if (!move_uploaded_file($temporario, $destino)) {
            throw new RuntimeException(
                'Não foi possível salvar um dos anexos.'
            );
        }

        $relativo = str_replace(
            __DIR__ . '/',
            '',
            $destino
        );

        $hash = hash_file('sha256', $destino);

        $stmt = $mysqli->prepare("
            INSERT INTO ti_impressora_anexos (
                impressora_id,
                manutencao_id,
                arquivo,
                nome_original,
                mime_type,
                tamanho_bytes,
                hash_sha256,
                enviado_por
            )
            VALUES (?, ?, ?, ?, ?, ?, ?, ?)
        ");

        $stmt->bind_param(
            'iisssiss',
            $impressoraId,
            $manutencaoId,
            $relativo,
            $nomeOriginal,
            $mime,
            $tamanho,
            $hash,
            $usuario
        );

        if (!$stmt->execute()) {
            @unlink($destino);
            $erro = $stmt->error;
            $stmt->close();

            throw new RuntimeException(
                'Não foi possível registrar o anexo: ' . $erro
            );
        }

        $stmt->close();
        $salvos++;
    }

    return $salvos;
}
