<?php
/**
 * renomear_logs.php
 * =================
 * Lê cada arquivo .log, extrai o CALLSIGN do cabeçalho Cabrillo e renomeia
 * o arquivo para {CALLSIGN}.log  (barras viram underscore: PY6MV/P → PY6MV_P)
 *
 * Uso:
 *   php renomear_logs.php --dir="C:\caminho\2024"                              # dry-run
 *   php renomear_logs.php --dir="C:\caminho\2024" --executar                   # renomeia
 *   php renomear_logs.php --dir="C:\caminho\2024" --executar --deletar-dup     # renomeia + apaga duplicatas
 *   php renomear_logs.php --dir="C:\caminho\2024" --deletar-dup               # dry-run mostrando o que apagaria
 *
 * A flag --deletar-dup apaga os arquivos com nome diferente do CALLSIGN quando
 * já existe um arquivo {CALLSIGN}.log no diretório (i.e., o original correto já
 * está lá e o atual é a cópia redundante com nome errado).
 *
 * Saída:
 *   - Tabela de todos os arquivos com status
 *   - Relatório separado dos NÃO renomeados com motivo detalhado
 *   - Log salvo em renomear_logs_YYYYMMDD_HHiiss.txt no mesmo diretório
 */
 
// ── Argumentos ────────────────────────────────────────────────────────────────
$args        = getopt('', ['dir:', 'executar', 'deletar-dup']);
$executar    = isset($args['executar']);
$deletarDup  = isset($args['deletar-dup']);
$dir         = isset($args['dir']) ? rtrim($args['dir'], '/\\') : rtrim(__DIR__, '/\\');
 
if (!is_dir($dir)) {
    echo "ERRO: Diretório não encontrado: {$dir}\n";
    exit(1);
}
 
// ── Coletar todos os .log recursivamente (1 nível de subpasta) ────────────────
$arquivos = [];
foreach (glob($dir . DIRECTORY_SEPARATOR . '*.log') ?: [] as $f) {
    $arquivos[] = $f;
}
// Subpastas de ano (ex: 2024/)
foreach (glob($dir . DIRECTORY_SEPARATOR . '*', GLOB_ONLYDIR) ?: [] as $sub) {
    foreach (glob($sub . DIRECTORY_SEPARATOR . '*.log') ?: [] as $f) {
        $arquivos[] = $f;
    }
}
sort($arquivos);
 
$total = count($arquivos);
 
// ── Buffer de saída (vai para tela E para arquivo de log) ─────────────────────
$output = '';
$logFile = $dir . DIRECTORY_SEPARATOR . 'renomear_logs_' . date('Ymd_His') . '.txt';
 
function out(string $linha = '') {
    global $output;
    $output .= $linha . "\n";
    echo $linha . "\n";
}
 
// ── Cabeçalho ─────────────────────────────────────────────────────────────────
out(str_repeat('=', 80));
out('LABRE-DX Contest — Renomeação de LOGs pelo CALLSIGN');
out(str_repeat('=', 80));
$modoLabel = match(true) {
    $executar && $deletarDup => '⚡ EXECUÇÃO REAL + deletar duplicatas',
    $executar                => '⚡ EXECUÇÃO REAL',
    $deletarDup              => '👁  DRY-RUN (mostrando o que seria apagado)',
    default                  => '👁  DRY-RUN (use --executar para renomear)',
};
out('Modo:      ' . $modoLabel);
out('Diretório: ' . $dir);
out('Arquivos:  ' . $total);
out(str_repeat('-', 80));
out(sprintf("%-40s %-14s %-30s %s", 'ARQUIVO ATUAL', 'CALLSIGN', 'NOVO NOME', 'STATUS'));
out(str_repeat('-', 80));
 
// ── Contadores e listas ────────────────────────────────────────────────────────
$renomeados   = 0;
$ja_ok        = 0;
$nao_renomeados = []; // ['arquivo' => ..., 'motivo' => ..., 'callsign' => ...]
 
foreach ($arquivos as $caminho) {
    $nomeAtual   = basename($caminho);
    $dirAtual    = dirname($caminho);
    $subpasta    = ($dirAtual !== $dir) ? basename($dirAtual) . '/' : '';
 
    // ── Extrair CALLSIGN com múltiplas estratégias ────────────────────────────
    [$callsign, $estrategia, $raw] = extrairCallsign($caminho);
 
    // ── Sem CALLSIGN encontrado ───────────────────────────────────────────────
    if (!$callsign) {
        $motivo = $raw
            ? "CALLSIGN encontrado mas inválido: \"{$raw}\""
            : "Linha CALLSIGN não encontrada (verificar encoding ou formato)";
        out(sprintf("%-40s %-14s %-30s %s",
            substr($subpasta . $nomeAtual, 0, 39), '?', '-', '⚠ ' . substr($motivo, 0, 35)));
        $nao_renomeados[] = [
            'arquivo'  => $subpasta . $nomeAtual,
            'callsign' => $raw ?: 'não encontrado',
            'motivo'   => $motivo,
            'estrategia' => $estrategia,
        ];
        continue;
    }
 
    $novoNome    = $callsign . '.log';
    $novoCaminho = $dirAtual . DIRECTORY_SEPARATOR . $novoNome;
 
    // ── Já tem o nome correto ─────────────────────────────────────────────────
    if ($nomeAtual === $novoNome) {
        out(sprintf("%-40s %-14s %-30s %s",
            substr($subpasta . $nomeAtual, 0, 39), $callsign, '—', '✓ Já OK'));
        $ja_ok++;
        continue;
    }
 
    // ── Conflito de nome no destino (duplicata) ────────────────────────────────
    if (file_exists($novoCaminho) && realpath($novoCaminho) !== realpath($caminho)) {
        $motivo = "Já existe {$novoNome} no diretório (duplicata)";
 
        if ($executar && $deletarDup) {
            if (@unlink($caminho)) {
                out(sprintf("%-40s %-14s %-30s %s",
                    substr($subpasta . $nomeAtual, 0, 39), $callsign, $novoNome, '🗑  Apagado (duplicata)'));
                $nao_renomeados[] = [
                    'arquivo'    => $subpasta . $nomeAtual,
                    'callsign'   => $callsign,
                    'motivo'     => 'Apagado — duplicata de ' . $novoNome,
                    'estrategia' => $estrategia,
                    'apagado'    => true,
                ];
            } else {
                out(sprintf("%-40s %-14s %-30s %s",
                    substr($subpasta . $nomeAtual, 0, 39), $callsign, $novoNome, '❌ Falha ao apagar'));
                $nao_renomeados[] = [
                    'arquivo'    => $subpasta . $nomeAtual,
                    'callsign'   => $callsign,
                    'motivo'     => 'Falha ao apagar duplicata: ' . (error_get_last()['message'] ?? '?'),
                    'estrategia' => $estrategia,
                    'apagado'    => false,
                ];
            }
        } elseif ($deletarDup) {
            out(sprintf("%-40s %-14s %-30s %s",
                substr($subpasta . $nomeAtual, 0, 39), $callsign, $novoNome, '🗑  Seria apagado (duplicata)'));
            $nao_renomeados[] = [
                'arquivo'    => $subpasta . $nomeAtual,
                'callsign'   => $callsign,
                'motivo'     => 'Seria apagado — duplicata de ' . $novoNome,
                'estrategia' => $estrategia,
                'apagado'    => null,
            ];
        } else {
            out(sprintf("%-40s %-14s %-30s %s",
                substr($subpasta . $nomeAtual, 0, 39), $callsign, $novoNome, '⚠ CONFLITO (use --deletar-dup)'));
            $nao_renomeados[] = [
                'arquivo'    => $subpasta . $nomeAtual,
                'callsign'   => $callsign,
                'motivo'     => $motivo,
                'estrategia' => $estrategia,
                'apagado'    => false,
            ];
        }
        continue;
    }
 
    // ── Renomear ──────────────────────────────────────────────────────────────
    if ($executar) {
        if (@rename($caminho, $novoCaminho)) {
            out(sprintf("%-40s %-14s %-30s %s",
                substr($subpasta . $nomeAtual, 0, 39), $callsign, $novoNome, '✅ Renomeado'));
            $renomeados++;
        } else {
            $motivo = error_get_last()['message'] ?? 'Falha desconhecida (permissão?)';
            out(sprintf("%-40s %-14s %-30s %s",
                substr($subpasta . $nomeAtual, 0, 39), $callsign, $novoNome, '❌ FALHA'));
            $nao_renomeados[] = [
                'arquivo'    => $subpasta . $nomeAtual,
                'callsign'   => $callsign,
                'motivo'     => $motivo,
                'estrategia' => $estrategia,
            ];
        }
    } else {
        out(sprintf("%-40s %-14s %-30s %s",
            substr($subpasta . $nomeAtual, 0, 39), $callsign, $novoNome, '→ Seria renomeado'));
        $renomeados++;
    }
}
 
// ── Resumo geral ──────────────────────────────────────────────────────────────
out('');
out(str_repeat('=', 80));
out('RESUMO');
out(str_repeat('-', 80));
$apagados       = count(array_filter($nao_renomeados, fn($i) => ($i['apagado'] ?? false) === true));
$apagadosSeria  = count(array_filter($nao_renomeados, fn($i) => ($i['apagado'] ?? false) === null));
$conflitos      = count(array_filter($nao_renomeados, fn($i) => !isset($i['apagado']) || $i['apagado'] === false));
 
out("Total de arquivos:        {$total}");
if ($executar) {
    out("✅ Renomeados:            {$renomeados}");
    out("✓  Já OK (sem alteração): {$ja_ok}");
    if ($apagados)  out("🗑  Apagados (duplicatas): {$apagados}");
    $restantes = count($nao_renomeados) - $apagados;
    if ($restantes) out("⚠  Pendentes/erros:       {$restantes}");
} else {
    out("→  Seriam renomeados:     {$renomeados}");
    out("✓  Já OK (sem alteração): {$ja_ok}");
    if ($apagadosSeria) out("🗑  Seriam apagados (dup): {$apagadosSeria}");
    $restantes = count($nao_renomeados) - $apagadosSeria;
    if ($restantes)     out("⚠  Conflitos/erros:       {$restantes}");
}
 
// ── Relatório detalhado dos NÃO renomeados ────────────────────────────────────
if (!empty($nao_renomeados)) {
    out('');
    out(str_repeat('=', 80));
    out('RELATÓRIO: ARQUIVOS NÃO RENOMEADOS (' . count($nao_renomeados) . ')');
    out(str_repeat('-', 80));
    out(sprintf("%-5s %-40s %-16s %s", 'SEQ', 'ARQUIVO', 'CALLSIGN', 'MOTIVO'));
    out(str_repeat('-', 80));
    foreach ($nao_renomeados as $i => $item) {
        out(sprintf("%-5d %-40s %-16s %s",
            $i + 1,
            substr($item['arquivo'], 0, 39),
            substr($item['callsign'], 0, 15),
            $item['motivo']
        ));
    }
 
    // Agrupamento por tipo de motivo
    out('');
    out('--- Agrupado por motivo ---');
    $grupos = [];
    foreach ($nao_renomeados as $item) {
        $tipo = substr($item['motivo'], 0, 40);
        $grupos[$tipo][] = $item['arquivo'];
    }
    foreach ($grupos as $tipo => $lista) {
        out(count($lista) . 'x — ' . $tipo);
        foreach ($lista as $arq) {
            out("       {$arq}");
        }
    }
}
 
if (!$executar && $renomeados > 0) {
    out('');
    out('Execute com --executar para renomear de fato.');
}
 
out('');
out(str_repeat('=', 80));
 
// ── Salvar log em arquivo ─────────────────────────────────────────────────────
file_put_contents($logFile, $output);
echo "\n📄 Log salvo em: " . basename($logFile) . "\n";
 
exit(empty($nao_renomeados) ? 0 : 1);
 
 
// ═══════════════════════════════════════════════════════════════════════════════
// Extrai CALLSIGN com múltiplas estratégias de fallback
// Retorna: [callsign_normalizado|null, estrategia_usada, valor_raw]
// ═══════════════════════════════════════════════════════════════════════════════
function extrairCallsign(string $caminho): array
{
    $raw = null;
 
    try {
        // Ler até 80 linhas; tentar múltiplos encodings
        $content = file_get_contents($caminho, false, null, 0, 8192);
        if ($content === false) {
            return [null, 'erro_leitura', null];
        }
 
        // Detectar e converter encoding
        $enc = mb_detect_encoding($content, ['UTF-8', 'ISO-8859-1', 'WINDOWS-1252', 'ASCII'], true);
        if ($enc && $enc !== 'UTF-8') {
            $content = mb_convert_encoding($content, 'UTF-8', $enc);
        }
 
        // Normalizar quebras de linha (CRLF, CR, LF)
        $content = str_replace(["\r\n", "\r"], "\n", $content);
        $linhas  = explode("\n", $content);
 
        foreach ($linhas as $i => $linha) {
            if ($i > 80) break;
 
            $linha = trim($linha);
            if ($linha === '') continue;
 
            // Estratégia 1: padrão exato "CALLSIGN: XXXXX"
            if (preg_match('/^CALLSIGN\s*:\s*(.+)$/i', $linha, $m)) {
                $raw = trim($m[1]);
                // Remover comentários após espaço (ex: "PY6MV ;comentario")
                $raw = preg_split('/[\s;#]/', $raw)[0];
                $raw = strtoupper($raw);
                // Validar: deve ter ao menos 3 chars alfanuméricos
                if (preg_match('/^[A-Z0-9][A-Z0-9\/\-]{2,}$/', $raw)) {
                    return [normalizarCall($raw), 'padrao', $raw];
                }
                // Callsign encontrado mas com chars estranhos — tentar limpar
                $limpo = preg_replace('/[^A-Z0-9\/]/', '', $raw);
                if (strlen($limpo) >= 3) {
                    return [normalizarCall($limpo), 'padrao_limpo', $raw];
                }
                // Inválido mesmo assim
                return [null, 'padrao_invalido', $raw];
            }
 
            // Estratégia 2: "CALL: XXXXX" (formato alternativo raro)
            if (preg_match('/^CALL\s*:\s*([A-Z0-9\/]{3,})\s*$/i', $linha, $m)) {
                $raw = strtoupper(trim($m[1]));
                return [normalizarCall($raw), 'call_alternativo', $raw];
            }
 
            // Estratégia 3: linha QSO — extrair call_sent (coluna 5 do Cabrillo)
            // QSO: freq modo data hora call_sent rst_sent exch ...
            if (preg_match('/^QSO:\s+\S+\s+\S+\s+\S+\s+\S+\s+([A-Z0-9\/]{3,})\s/i', $linha, $m)) {
                $raw = strtoupper($m[1]);
                return [normalizarCall($raw), 'qso_call_sent', $raw];
            }
        }
    } catch (Throwable $e) {
        return [null, 'excecao: ' . $e->getMessage(), null];
    }
 
    return [null, 'nao_encontrado', $raw];
}
 
// Normaliza callsign: maiúsculas, barra → underscore
function normalizarCall(string $call): string
{
    $call = strtoupper(trim($call));
    $call = str_replace('/', '_', $call);   // PY6MV/P → PY6MV_P
    $call = preg_replace('/[^A-Z0-9_]/', '', $call);
    return $call;
}