<?php
namespace App\Http\Controllers\Processamento;
 
use App\Http\Controllers\Controller;
use App\Models\ContestDate;
use App\Models\LogCabecalho;
use App\Models\LogQso;
use App\Models\ContestEtapa;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
 
class ImportacaoController extends Controller
{
    public function index()
    {
        return view('processamento.importar');
    }
 
    public function confirmar()
    {
        $dados = [
            'total_cabecalhos' => LogCabecalho::count(),
            'total_qsos'       => LogQso::count(),
            'contest'          => ContestDate::ativo(),
        ];
        return view('processamento.importar-confirmar', compact('dados'));
    }
 
    public function executar(Request $request)
    {
        $request->validate([
            'confirmacao' => ['required', 'in:CONFIRMAR'],
            'contest_id'  => ['required', 'integer', 'exists:contest_date,id'],
        ], [
            'confirmacao.in'    => 'Digite exatamente CONFIRMAR (maiúsculas) para prosseguir.',
            'contest_id.exists' => 'Contest inválido.',
        ]);
 
        // Inicializa já como 'processando' — bloqueia menu imediatamente
        session([
            'imp_contest_id' => $request->integer('contest_id'),
            'imp_status'     => 'processando',
            'imp_progresso'  => [],
            'imp_total'      => 0,
            'imp_atual'      => 0,
            'imp_concluido'  => false,
        ]);
 
        return redirect()->route('processamento.importar.progresso');
    }
 
    /** Página da barra de progresso */
    public function progresso()
    {
        if (!session('imp_contest_id')) {
            return redirect()->route('processamento.importar')
                             ->with('erro', 'Inicie o processo pela tela de importação.');
        }
        return view('processamento.importar-progresso');
    }
 
    /**
     * GET /importar/status — consultado pelo JS a cada segundo via fetch().
     * Retorna o estado atual do progresso gravado na sessão.
     */
    public function status()
    {
        return response()->json([
            'status'     => session('imp_status',    'aguardando'),
            'total'      => session('imp_total',     0),
            'atual'      => session('imp_atual',     0),
            'progresso'  => session('imp_progresso', []),
            'concluido'  => session('imp_concluido', false),
            'resultado'  => session('imp_resultado', null),
        ]);
    }
 
    /**
     * POST /importar/processar — executa a importação de fato.
     * Chamado via fetch() pelo JavaScript logo após a página de progresso carregar.
     * Atualiza a sessão a cada arquivo para que /status reflita o andamento.
     */
    public function processar(Request $request)
    {
        $contestId = session('imp_contest_id');
        if (!$contestId) {
            return response()->json(['erro' => 'Sessao nao encontrada.'], 403);
        }
 
        // Garante status 'processando' para bloquear menu durante execução
        session()->put('imp_status', 'processando');
        session()->save();
 
        $ini = microtime(true);
        $log = [];
 
        // Desabilita FK checks UMA VEZ para todo o processo (não por arquivo)
        // ── 1. Limpar tabelas ────────────────────────────────────────────────
        $this->atualizarSessao('limpando', 0, 0, $log);
        $log[] = ['msg' => 'Esvaziando tabelas...', 'tipo' => 'info'];
        $this->atualizarSessao('limpando', 0, 0, $log);
 
        DB::statement('SET FOREIGN_KEY_CHECKS = 0');
        DB::table('logs_qsos')->delete();
        DB::table('logs_cabecalhos')->delete();
        DB::statement('ALTER TABLE logs_qsos AUTO_INCREMENT = 1');
        DB::statement('ALTER TABLE logs_cabecalhos AUTO_INCREMENT = 1');
        DB::statement('SET FOREIGN_KEY_CHECKS = 1');
 
        $log[] = ['msg' => 'Tabelas limpas com sucesso.', 'tipo' => 'ok'];
        $this->atualizarSessao('limpando', 0, 0, $log);
 
        activity()->causedBy(auth()->user())
                  ->log('Importação iniciada — contest_id: ' . $contestId);
 
        // ── 2. Listar arquivos ───────────────────────────────────────────────
        // Diretório: inputs/LOGsRecebidosPorAno/{ano}/
        $ano      = \Carbon\Carbon::parse(ContestDate::ativo()?->start_date)->year
                    ?? date('Y');
        $dir      = config('contest.logs_base_dir') . DIRECTORY_SEPARATOR . $ano;
        $arquivos = glob($dir . DIRECTORY_SEPARATOR . '*.log') ?: [];
        $total    = count($arquivos);
 
        if ($total === 0) {
            $log[] = ['msg' => "❌ Nenhum arquivo .log encontrado em: {$dir}", 'tipo' => 'erro'];
            $this->atualizarSessao('erro', 0, 0, $log);
            return response()->json(['ok' => false]);
        }
 
        $log[] = ['msg' => "Encontrados {$total} arquivo(s). Iniciando...", 'tipo' => 'info'];
        $this->atualizarSessao('processando', $total, 0, $log);
 
        // ── 3. Processar cada arquivo ────────────────────────────────────────
        $processados   = 0;
        $erros         = [];
        $logsVistos    = []; // callsign => nome do arquivo (para detectar duplicatas)
        $duplicatas    = []; // arquivos com mesmo callsign
 
        // $logSessao: janela deslizante de 30 linhas para o terminal da view
        // $log: acumulação COMPLETA apenas para o resumo final (não vai para sessão durante o loop)
        $logSessao = [];
 
        foreach ($arquivos as $idx => $filePath) {
            $nome = basename($filePath);
            $atual = $idx + 1;
 
            try {
                [$header, $contacts] = $this->parseLogFile($filePath);
                $callsign = $header['callsign'] ?? '?';
 
                if (isset($logsVistos[$callsign])) {
                    $duplicatas[] = [
                        'callsign'   => $callsign,
                        'arquivo1'   => $logsVistos[$callsign],
                        'arquivo2'   => $nome,
                    ];
                    $linha = ['msg' => "DUP [{$atual}/{$total}] {$callsign} ja existe em {$logsVistos[$callsign]}", 'tipo' => 'dup'];
                } else {
                    $this->insertDataToDB($header, $contacts, $contestId);
                    $processados++;
                    $linha = ['msg' => "OK [{$atual}/{$total}] {$nome} ({$callsign}) — " . count($contacts) . " QSOs", 'tipo' => 'ok'];
                }
                $logsVistos[$callsign] = $nome;
 
            } catch (\Throwable $e) {
                $erros[] = "{$nome}: " . $e->getMessage();
                $linha = ['msg' => "ERRO [{$atual}/{$total}] {$nome}: " . $e->getMessage(), 'tipo' => 'erro'];
            }
 
            // Janela deslizante: mantém últimas 30 linhas na sessão
            $logSessao[] = $linha;
            if (count($logSessao) > 30) {
                array_shift($logSessao);
            }
 
            // Grava sessão a cada 5 arquivos
            if ($idx % 5 === 0 || $atual === $total) {
                $this->atualizarSessao('processando', $total, $atual, $logSessao);
            }
        }
 
        // ── 4. Resultado final ───────────────────────────────────────────────
        $fim    = microtime(true);
        $seg    = $fim - $ini;
        $tempo  = $seg >= 60
            ? number_format($seg / 60, 2) . ' min'
            : number_format($seg, 1) . ' seg';
 
        $log[] = ['msg' => "✅ Concluído em {$tempo}", 'tipo' => 'ok'];
 
        activity()->causedBy(auth()->user())
                  ->log("Importação concluída: {$processados} logs, " . count($duplicatas) . " duplicatas.");
 
        $resultado = [
            'processados' => $processados,
            'duplicatas'  => $duplicatas,
            'erros'       => $erros,
            'tempo'       => $tempo,
            'inicio'      => date('d/m/Y H:i:s', (int)$ini),
            'fim'         => date('d/m/Y H:i:s', (int)$fim),
        ];
 
        // Registra conclusão da Etapa A
        \App\Models\ContestEtapa::concluir('A', [
            'total_processados' => $processados,
            'total_invalidos'   => count($erros),
            'tempo_gasto'       => $tempo,
        ]);
 
        // Sessao compacta — put() separados para evitar falha silenciosa
        session()->put('imp_status',    'concluido');
        session()->put('imp_total',     $total);
        session()->put('imp_atual',     $total);
        // Progresso final = últimas linhas do terminal + conclusão
        $logFinal = $logSessao; // últimas 30 linhas do processamento
        $logFinal[] = ['msg' => "Concluido em {$tempo} | {$processados} LOGs importados", 'tipo' => 'ok'];
        session()->put('imp_progresso', $logFinal);
        session()->put('imp_concluido', true);
        session()->put('imp_resultado', $resultado);
        session()->forget('imp_contest_id');
        session()->save();
 
        return response()->json(['ok' => true]);
    }
 
    // ── helpers de sessão ────────────────────────────────────────────────────
 
    private function atualizarSessao(string $status, int $total, int $atual, array $log): void
    {
        session()->put('imp_status',    $status);
        session()->put('imp_total',     $total);
        session()->put('imp_atual',     $atual);
        session()->put('imp_progresso', array_slice($log, -30)); // limita a 30 linhas
        session()->save();
    }
 
    // ── parser Cabrillo ──────────────────────────────────────────────────────
 
    private function parseLogFile(string $filePath): array
    {
        $content = file_get_contents($filePath);
        $enc     = mb_detect_encoding($content, ['UTF-8', 'ISO-8859-1', 'WINDOWS-1252'], true);
        $content = mb_convert_encoding($content, 'UTF-8', $enc ?: 'UTF-8');
        $lines   = explode("\n", $content);
 
        $header   = [];
        $contacts = [];
        $inQso    = false;
 
        foreach ($lines as $line) {
            $line = trim($line);
            if (str_starts_with($line, 'QSO:')) {
                $inQso = true;
                $parts = preg_split('/\s+/', $line);
                if (count($parts) >= 10) {
                    $contacts[] = [
                        'frequency' => $parts[1] ?? '',
                        'mode'      => $parts[2] ?? '',
                        'date_time' => date('Y-m-d H:i:s', strtotime(($parts[3] ?? '') . ' ' . ($parts[4] ?? '00:00'))),
                        'call_sent' => $parts[5] ?? '',
                        'rst_sent'  => $parts[6] ?? '',
                        'exch_sent' => $parts[7] ?? '',
                        'call_rcvd' => $parts[8] ?? '',
                        'rst_rcvd'  => $parts[9] ?? '',
                        'exch_rcvd' => $parts[10] ?? '',
                    ];
                }
            } elseif (!$inQso && str_contains($line, ': ')) {
                [$k, $v] = explode(': ', $line, 2);
                $header[strtolower(str_replace('-', '_', $k))] = trim($v);
            }
        }
 
        $header   = $this->normalizeKeys($header);
        $contacts = array_map(fn($c) => array_map([$this, 'cleanField'], $c), $contacts);
        $header   = array_map(fn($v) => is_string($v) ? $this->cleanField($v) : $v, $header);
 
        return [$header, $contacts];
    }
 
    private function insertDataToDB(array $header, array $contacts, int $contestId): void
    {
        $defaults = [
            'start_of_log' => '', 'arrl_section' => '', 'callsign' => '',
            'contest' => '', 'contest_id' => $contestId,
            'category_operator' => '', 'category_assisted' => '',
            'category_band' => '', 'category_power' => '', 'category_station' => '',
            'category_mode' => '', 'category_transmitter' => '',
            'category_overlay' => '', 'category_time' => '',
            'claimed_score' => 0, 'claimed_calculado' => 0, 'final_score' => 0,
            'name' => '', 'location' => '', 'address' => '',
            'address_city' => '', 'address_state_province' => '',
            'address_postalcode' => '', 'address_country' => '',
            'operators' => '', 'email' => '', 'grid_locator' => '',
            'club' => '', 'certificate' => '', 'created_by' => '',
            'soapbox' => '', 'data_de_nascimento' => null, 'x_source' => '', 'end_of_log' => '',
        ];
 
        $h = array_merge($defaults, $header);
        $h['contest_id']         = $contestId;
        $h['location']           = substr($h['location']   ?? '', 0, 30);
        $h['created_by']         = substr($h['created_by'] ?? '', 0, 50);
        $h['soapbox']            = substr($h['soapbox']    ?? '', 0, 50);
        $h['data_de_nascimento'] = empty($h['data_de_nascimento']) ? null : $h['data_de_nascimento'];
 
        $logId = DB::table('logs_cabecalhos')->insertGetId(array_intersect_key($h, $defaults));
 
        if (!empty($contacts)) {
            $qsoBase = [
                'log_id'    => $logId, 'contest_id' => $contestId,
                'frequency' => '', 'mode'      => '', 'date_time' => '',
                'call_sent' => '', 'rst_sent'  => '', 'exch_sent' => '',
                'call_rcvd' => '', 'rst_rcvd'  => '', 'exch_rcvd' => '',
            ];
 
            // Monta lote completo e insere de uma vez (1 query por LOG)
            $lote = [];
            foreach ($contacts as $c) {
                $lote[] = array_merge($qsoBase, $c, ['log_id' => $logId, 'contest_id' => $contestId]);
            }
            // Lotes de 500 para não exceder max_allowed_packet
            foreach (array_chunk($lote, 500) as $chunk) {
                try {
                    DB::table('logs_qsos')->insert($chunk);
                } catch (\Throwable) {
                    // Fallback: insere um a um se o lote falhar (duplicatas)
                    foreach ($chunk as $row) {
                        try { DB::table('logs_qsos')->insert($row); }
                        catch (\Throwable) {}
                    }
                }
            }
        }
    }
 
    private function normalizeKeys(array $header): array
    {
        $map = [
            'início_do_diário' => 'start_of_log', 'inÍcio_do_diÁrio' => 'start_of_log',
            'localização' => 'location', 'localizaÇÃo' => 'location',
            'clube' => 'club', 'concurso' => 'contest',
            'category' => 'category_operator', 'categoria_operador' => 'category_operator',
            'categoria_banda' => 'category_band', 'categoria_modo' => 'category_mode',
            'categoria_potÊncia' => 'category_power', 'pontuaÇÃo reivindicada' => 'claimed_score',
            'nome' => 'name', 'operadores' => 'operators', 'e_mail' => 'email',
            'data de nascimento' => 'data_de_nascimento', 'fim_do_diÁrio' => 'end_of_log',
        ];
        $out = [];
        foreach ($header as $k => $v) {
            $out[$map[strtolower($k)] ?? strtolower($k)] = $v;
        }
        return $out;
    }
 
    private function cleanField(string $value): string
    {
        if (function_exists('transliterator_transliterate')) {
            $value = transliterator_transliterate('Any-Latin; Latin-ASCII;', $value);
        }
        $value = preg_replace('/[\x00-\x1F\x7F]/u', '', $value);
        $value = preg_replace('/\s+/', ' ', $value);
        return strtoupper(trim($value));
    }
}