<?php
declare(strict_types=1);

/*
|--------------------------------------------------------------------------
| Administradores autorizados
|--------------------------------------------------------------------------
|
| O sistema atual não possui coluna de perfil na tabela usuarios.
| Por isso, a liberação do painel é feita pelo nome de usuário da sessão.
|
*/
function alertaAdministradoresPermitidos(): array
{
    return [
        'renan',
        'admin',
        'administrador'
    ];
}

function alertaTextoNormalizado(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 alertaUsuarioAtual(): string
{
    $usuario = trim((string) ($_SESSION['usuario'] ?? ''));

    return $usuario !== ''
        ? $usuario
        : 'Usuário autenticado';
}

function alertaUsuarioEhAdmin(): bool
{
    $camposPerfil = [
        $_SESSION['perfil'] ?? '',
        $_SESSION['tipo'] ?? '',
        $_SESSION['nivel'] ?? '',
        $_SESSION['role'] ?? ''
    ];

    foreach ($camposPerfil as $perfil) {
        $normalizado = alertaTextoNormalizado((string) $perfil);

        if (in_array(
            $normalizado,
            ['admin', 'administrador', 'superadmin'],
            true
        )) {
            return true;
        }
    }

    if (
        !empty($_SESSION['is_admin']) ||
        !empty($_SESSION['administrador'])
    ) {
        return true;
    }

    $usuario = alertaTextoNormalizado(alertaUsuarioAtual());

    $permitidos = array_map(
        'alertaTextoNormalizado',
        alertaAdministradoresPermitidos()
    );

    return in_array($usuario, $permitidos, true);
}

function alertaGarantirCsrf(): string
{
    if (session_status() !== PHP_SESSION_ACTIVE) {
        session_start();
    }

    if (empty($_SESSION['csrf_alerta_global'])) {
        $_SESSION['csrf_alerta_global'] =
            bin2hex(random_bytes(24));
    }

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

function alertaIpAtual(): string
{
    $ip = trim((string) ($_SERVER['REMOTE_ADDR'] ?? ''));

    return substr($ip, 0, 45);
}

function alertaClienteHash(): string
{
    $base = implode('|', [
        session_id(),
        (string) ($_SERVER['HTTP_USER_AGENT'] ?? ''),
        alertaUsuarioAtual()
    ]);

    return hash('sha256', $base);
}

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

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

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

/*
|--------------------------------------------------------------------------
| Armazenamento compartilhado sem banco de dados
|--------------------------------------------------------------------------
|
| O alerta é temporário. Portanto, ele é guardado no diretório temporário
| do servidor, que normalmente já é gravável pelo PHP/Apache.
|
| Isso evita:
| - CREATE TABLE na abertura da página;
| - dependência de permissão do usuário MySQL;
| - erro HTTP 500 quando a tabela ainda não existe;
| - necessidade de importar SQL.
|
*/
function alertaStoragePath(): string
{
    $identificador = substr(
        hash('sha256', __DIR__),
        0,
        18
    );

    return rtrim(
        sys_get_temp_dir(),
        DIRECTORY_SEPARATOR
    ) . DIRECTORY_SEPARATOR .
        'manutencao_ti_alerta_' .
        $identificador .
        '.json';
}

function alertaStoragePadrao(): array
{
    return [
        'versao' => 1,
        'proximo_id' => 1,
        'ativo' => null,
        'historico' => [],
        'presencas' => []
    ];
}

function alertaStorageNormalizar(array $dados): array
{
    $padrao = alertaStoragePadrao();

    $dados = array_replace(
        $padrao,
        $dados
    );

    if (!is_array($dados['historico'])) {
        $dados['historico'] = [];
    }

    if (!is_array($dados['presencas'])) {
        $dados['presencas'] = [];
    }

    if (
        $dados['ativo'] !== null &&
        !is_array($dados['ativo'])
    ) {
        $dados['ativo'] = null;
    }

    $dados['proximo_id'] = max(
        1,
        (int) ($dados['proximo_id'] ?? 1)
    );

    return $dados;
}

function alertaStorageAbrir()
{
    $caminho = alertaStoragePath();

    $arquivo = @fopen($caminho, 'c+');

    if ($arquivo === false) {
        throw new RuntimeException(
            'O PHP não conseguiu abrir o armazenamento temporário do alerta: ' .
            $caminho
        );
    }

    return $arquivo;
}

function alertaStorageLer(): array
{
    $arquivo = alertaStorageAbrir();

    try {
        if (!flock($arquivo, LOCK_SH)) {
            throw new RuntimeException(
                'Não foi possível bloquear o armazenamento para leitura.'
            );
        }

        rewind($arquivo);
        $conteudo = stream_get_contents($arquivo);

        $dados = [];

        if (is_string($conteudo) && trim($conteudo) !== '') {
            $decodificado = json_decode(
                $conteudo,
                true
            );

            if (is_array($decodificado)) {
                $dados = $decodificado;
            }
        }

        flock($arquivo, LOCK_UN);

        return alertaStorageNormalizar($dados);
    } finally {
        fclose($arquivo);
    }
}

function alertaStorageAlterar(callable $alteracao): mixed
{
    $arquivo = alertaStorageAbrir();

    try {
        if (!flock($arquivo, LOCK_EX)) {
            throw new RuntimeException(
                'Não foi possível bloquear o armazenamento para gravação.'
            );
        }

        rewind($arquivo);
        $conteudo = stream_get_contents($arquivo);

        $dados = [];

        if (is_string($conteudo) && trim($conteudo) !== '') {
            $decodificado = json_decode(
                $conteudo,
                true
            );

            if (is_array($decodificado)) {
                $dados = $decodificado;
            }
        }

        $dados = alertaStorageNormalizar($dados);

        $resultado = $alteracao($dados);

        $dados = alertaStorageNormalizar($dados);

        $json = json_encode(
            $dados,
            JSON_UNESCAPED_UNICODE |
            JSON_UNESCAPED_SLASHES |
            JSON_PRETTY_PRINT
        );

        if (!is_string($json)) {
            throw new RuntimeException(
                'Não foi possível gerar os dados do alerta.'
            );
        }

        rewind($arquivo);

        if (!ftruncate($arquivo, 0)) {
            throw new RuntimeException(
                'Não foi possível limpar o armazenamento anterior.'
            );
        }

        $gravado = fwrite($arquivo, $json);

        if ($gravado === false) {
            throw new RuntimeException(
                'Não foi possível salvar o alerta no servidor.'
            );
        }

        fflush($arquivo);
        flock($arquivo, LOCK_UN);

        return $resultado;
    } finally {
        fclose($arquivo);
    }
}

function alertaAgoraBanco(?int $timestamp = null): string
{
    return date(
        'Y-m-d H:i:s',
        $timestamp ?? time()
    );
}

function alertaEncerrarNoStorage(
    array &$dados,
    string $responsavel,
    ?int $agora = null
): bool {
    $agora ??= time();

    if (!is_array($dados['ativo'] ?? null)) {
        $dados['ativo'] = null;
        return false;
    }

    $alerta = $dados['ativo'];
    $alertaId = (int) ($alerta['id'] ?? 0);

    $alerta['ativo'] = 0;
    $alerta['encerrado_por'] = $responsavel;
    $alerta['encerrado_em'] = alertaAgoraBanco($agora);

    $alcancados = 0;

    foreach ((array) ($dados['presencas'] ?? []) as $presenca) {
        if (
            (int) ($presenca['alerta_id'] ?? 0) ===
            $alertaId
        ) {
            $alcancados++;
        }
    }

    $alerta['alcancados_total'] = max(
        (int) ($alerta['alcancados_total'] ?? 0),
        $alcancados
    );

    $encontrado = false;

    foreach ($dados['historico'] as &$historico) {
        if ((int) ($historico['id'] ?? 0) !== $alertaId) {
            continue;
        }

        $historico = array_replace(
            $historico,
            $alerta
        );

        $encontrado = true;
        break;
    }
    unset($historico);

    if (!$encontrado) {
        array_unshift(
            $dados['historico'],
            $alerta
        );
    }

    $dados['historico'] = array_slice(
        $dados['historico'],
        0,
        20
    );

    $dados['ativo'] = null;

    return true;
}

function alertaEncerrarExpirados(): void
{
    alertaStorageAlterar(
        static function (array &$dados): void {
            $alerta = $dados['ativo'] ?? null;

            if (!is_array($alerta)) {
                return;
            }

            $expiraTimestamp = (int) (
                $alerta['expira_timestamp'] ?? 0
            );

            if (
                $expiraTimestamp > 0 &&
                $expiraTimestamp <= time()
            ) {
                alertaEncerrarNoStorage(
                    $dados,
                    'Expiração automática'
                );
            }
        }
    );
}

/*
|--------------------------------------------------------------------------
| Compatibilidade com a primeira versão
|--------------------------------------------------------------------------
|
| Os arquivos antigos chamavam esta função passando o mysqli. Agora ela não
| precisa executar CREATE TABLE. Mantemos a função para evitar quebra caso
| alguma página antiga ainda a utilize.
|
*/
function alertaGarantirTabelas(...$ignorado): void
{
    // Não utiliza mais banco de dados.
}
