<?php
// ============================================================
//  lib/parser.php
// ============================================================

require_once __DIR__ . '/../config.php';
require_once __DIR__ . '/pdf_reader.php';

class ParserNotas {

    public static function parsearTexto(string $texto): array {
        if (strlen(trim($texto)) < 50) {
            throw new RuntimeException('Texto insuficiente para analizar.');
        }
        return self::procesarTexto($texto);
    }

    public static function parsear(string $ruta_pdf): array {
        $texto = PdfReader::getText($ruta_pdf);
        if (strlen(trim($texto)) < 50) {
            throw new RuntimeException('No se pudo extraer texto del PDF.');
        }
        return self::procesarTexto($texto);
    }

    private static function procesarTexto(string $texto): array {
        $paciente = self::extraerPaciente($texto);
        $bloques  = self::dividirBloques($texto);

        $notas   = [];
        $por_dia = [];

        foreach ($bloques as $bloque) {
            $nota = self::parsearBloque($bloque);
            if (!$nota) continue;
            $notas[]                        = $nota;
            $por_dia[$nota['fecha_grab']][] = $nota;
        }

        if (empty($notas)) {
            throw new RuntimeException(
                'No se encontraron notas de enfermería en el PDF. ' .
                'Verifique que sea un archivo R-FAST válido.'
            );
        }

        // Calcula, por día, a qué nota específica corresponde cada turno faltante
        // (la primera nota que aparece cronológicamente después del hueco).
        $faltantes_por_nota = [];
        foreach ($por_dia as $notas_dia) {
            $faltantes_por_nota += self::hallazgosTurnoPorNota($notas_dia);
        }

        $resultados = [];
        foreach ($notas as $nota) {
            $faltantes = $faltantes_por_nota[$nota['id_nota']] ?? [];
            $resultados[] = self::aplicarReglas($nota, $faltantes);
        }

        return [
            'paciente'   => $paciente,
            'total'      => count($resultados),
            'hallazgos'  => count(array_filter($resultados, fn($r) => $r['estado'] === 'HALLAZGO')),
            'resultados' => $resultados,
        ];
    }

    private static function dividirBloques(string $texto): array {
        $partes = preg_split('/\*{2,}\s*NOTAS\s+DE\s+ENFERMERIA/iu', $texto);
        if (count($partes) > 1) {
            array_shift($partes);
            $r = array_values(array_filter($partes, fn($p) => strlen(trim($p)) > 20));
            if (!empty($r)) return $r;
        }

        $partes = preg_split('/\bNOTAS\s+DE\s+ENFERMERIA\b/iu', $texto);
        if (count($partes) > 1) {
            array_shift($partes);
            $r = array_values(array_filter($partes, fn($p) => strlen(trim($p)) > 20));
            if (!empty($r)) return $r;
        }

        $partes = preg_split('/(?=\bId:\s{1,6}\d{5,9}\b)/iu', $texto);
        if (count($partes) > 1) {
            array_shift($partes);
            return array_values(array_filter($partes, fn($p) => strlen(trim($p)) > 20));
        }

        return [];
    }

    private static function parsearBloque(string $bloque): ?array {

        if (!preg_match('/\bId:\s{1,6}(\d{5,9})\b/iu', $bloque, $m_id)) return null;

        // FLAG /u para UTF-8 — crítico para que [oó] haga match con ó (c3b3)
        if (!preg_match(
            '/Fecha\s+de\s+grabaci[oó]n\s*:\s*(\d{1,2}-\w{3,4}-\d{4})\s+(\d{1,2}:\d{2})\s*(am|pm)/iu',
            $bloque, $m_grab
        )) return null;

        $fecha_grab = self::normalizarFecha($m_grab[1]);
        $ampm_grab  = strtolower($m_grab[3]);
        $hora_grab  = self::horaA24h($m_grab[2], $ampm_grab);

        $fecha_aten = null; $hora_aten = null; $ampm_aten = null;
        if (preg_match(
            '/Fecha\s+y\s+Hora\s+de\s+Atencion\s*:+\s*(\d{1,2}-\w{3,4}-\d{4})\s+(\d{1,2}:\d{2})\s*(am|pm)/iu',
            $bloque, $m_aten
        )) {
            $fecha_aten = self::normalizarFecha($m_aten[1]);
            $ampm_aten  = strtolower($m_aten[3]);
            $hora_aten  = self::horaA24h($m_aten[2], $ampm_aten);
        }

        $profesional = 'No especificado';
        if (preg_match(
            '/PROFESIONAL\s*:\s*\[\d+\]\s*[\w_\-]+\s*-\s*([\wÁÉÍÓÚáéíóúÑñ\s\.]+?)\s*-\s*Identificaci[oó]n/iu',
            $bloque, $m_prof
        )) {
            $profesional = trim($m_prof[1]);
        }

        return [
            'id_nota'     => $m_id[1],
            'profesional' => $profesional,
            'fecha_grab'  => $fecha_grab,
            'hora_grab'   => $hora_grab,
            'ampm_grab'   => $ampm_grab,
            'fecha_aten'  => $fecha_aten,
            'hora_aten'   => $hora_aten,
            'ampm_aten'   => $ampm_aten,
        ];
    }

    private static function aplicarReglas(array $nota, array $faltantes): array {
        $r2_ok  = ($nota['fecha_aten'] !== null && $nota['fecha_aten'] === $nota['fecha_grab']);
        $r2_det = $r2_ok ? null : "Grab: {$nota['fecha_grab']} → Aten: " . ($nota['fecha_aten'] ?? 'no registrada');

        $r3_ok  = ($nota['ampm_aten'] !== null && $nota['ampm_grab'] === $nota['ampm_aten']);
        $r3_det = $r3_ok ? null : "Grab: {$nota['hora_grab']} → Aten: " . ($nota['hora_aten'] ?? 'no registrada');

        $r1_ok  = empty($faltantes);
        $r1_det = $r1_ok ? null : 'Falta: ' . implode(', ', $faltantes);

        $num_h = (int)!$r1_ok + (int)!$r2_ok + (int)!$r3_ok;

        return array_merge($nota, [
            'r1_ok'         => $r1_ok,  'r1_detalle' => $r1_det,
            'r2_ok'         => $r2_ok,  'r2_detalle' => $r2_det,
            'r3_ok'         => $r3_ok,  'r3_detalle' => $r3_det,
            'num_hallazgos' => $num_h,
            'estado'        => $num_h > 0 ? 'HALLAZGO' : 'OK',
        ]);
    }

    // Ordinal de turno según la hora real de grabación (0=mañana,1=tarde,2=noche).
    // Devuelve null si la hora no cae en ninguna de las 3 franjas (p.ej. 16:00-18:59).
    private static function turnoOrdinal(array $n): ?int {
        if ($n['ampm_grab'] === 'am') return 0;
        // hora_grab ya está en formato 24h "13:00:00"
        $h = (int)explode(':', $n['hora_grab'])[0];
        if ($h >= 13 && $h <= 15) return 1;
        if ($h >= 19 || $h === 0)  return 2;
        return null;
    }

    // Recorre las notas de un día en orden cronológico y detecta huecos de turno
    // (mañana -> tarde -> noche). El hallazgo se atribuye a la PRIMERA nota que
    // aparece después del hueco (la nota que "debería haber sabido" que faltó el
    // turno anterior). Si el hueco queda al final del día sin nota posterior,
    // se atribuye a la última nota del día como respaldo.
    // Devuelve: [id_nota => ['turno tarde', ...]]
    private static function hallazgosTurnoPorNota(array $notas_dia): array {
        $nombres = [0 => 'turno mañana', 1 => 'turno tarde', 2 => 'turno noche'];

        $ordenadas = $notas_dia;
        usort($ordenadas, fn($a, $b) => strcmp($a['hora_grab'], $b['hora_grab']));

        $mapa      = [];
        $last_seen = -1;

        foreach ($ordenadas as $n) {
            $ord = self::turnoOrdinal($n);
            if ($ord === null) continue; // esta nota no pertenece a ninguna franja de turno

            if ($ord > $last_seen + 1) {
                $faltantes = [];
                for ($i = $last_seen + 1; $i < $ord; $i++) {
                    $faltantes[] = $nombres[$i];
                }
                $mapa[$n['id_nota']] = $faltantes;
            }
            $last_seen = max($last_seen, $ord);
        }

        // Turno(s) que quedaron faltando al cierre del día (no hay nota posterior
        // que "reciba" el hallazgo) -> se asignan a la última nota del día.
        if ($last_seen < 2 && !empty($ordenadas)) {
            $faltantes_final = [];
            for ($i = $last_seen + 1; $i <= 2; $i++) {
                $faltantes_final[] = $nombres[$i];
            }
            $ultima     = end($ordenadas);
            $existentes = $mapa[$ultima['id_nota']] ?? [];
            $mapa[$ultima['id_nota']] = array_merge($existentes, $faltantes_final);
        }

        return $mapa;
    }

    private static function normalizarFecha(string $f): string {
        static $meses = [
            'ene'=>'01','feb'=>'02','mar'=>'03','abr'=>'04',
            'may'=>'05','jun'=>'06','jul'=>'07','ago'=>'08',
            'sep'=>'09','oct'=>'10','nov'=>'11','dic'=>'12',
        ];
        if (preg_match('/(\d{1,2})-(\w{3,4})-(\d{4})/u', $f, $m)) {
            $mes = $meses[strtolower(substr($m[2], 0, 3))] ?? '00';
            return sprintf('%s-%s-%02d', $m[3], $mes, (int)$m[1]);
        }
        return $f;
    }

    // Convierte "08:31" + "am"/"pm" a "08:31:00" formato MySQL TIME
    private static function horaA24h(string $hora, string $ampm): string {
        [$h, $m] = explode(':', $hora);
        $h = (int)$h;
        $m = (int)$m;
        if ($ampm === 'pm' && $h !== 12) $h += 12;
        if ($ampm === 'am' && $h === 12) $h = 0;
        return sprintf('%02d:%02d:00', $h, $m);
    }

    private static function extraerPaciente(string $texto): array {
        $cc = $nombre = '';
        if (preg_match(
            '/Identificaci[oó]n:\s*CC\s+(\d+)\s{1,6}([\wÁÉÍÓÚáéíóúÑñ\s]+?)\s{1,6}[HM],/u',
            $texto, $m
        )) {
            $cc     = trim($m[1]);
            $nombre = trim(preg_replace('/\s+/u', ' ', $m[2]));
        }
        return ['cc' => $cc, 'nombre' => $nombre];
    }
}
