from __future__ import annotations

import re
import unicodedata

from app.services.historia_html_legacy import extract_historia_resumen_from_html


_SUMMARY_HEADERS = (
    "resumen clinico",
    "resumen clínico",
    "resumen",
    "enfermedad actual",
)
_MOTIVO_HEADERS = ("motivo de consulta", "motivo consulta", "motivo")
_DIAGNOSTICO_HEADERS = ("diagnosticos", "diagnósticos", "diagnostico", "diagnóstico")
_PROCEDIMIENTO_HEADERS = (
    "procedimientos realizados",
    "procedimientos",
    "procedimiento realizado",
    "descripcion del procedimiento",
    "descripción del procedimiento",
    "procedimiento principal",
)
_MEDICAMENTO_HEADERS = (
    "medicamentos administrados",
    "medicamentos formulados",
    "medicamentos",
    "tratamiento farmacologico",
    "tratamiento farmacológico",
)
_IDENTITY_HEADERS = (
    "nombre del paciente",
    "paciente",
    "documento",
    "identificacion",
    "identificación",
    "caso",
    "fecha de ingreso",
    "fecha ingreso",
    "fecha",
    "eps",
    "aseguradora",
    "pagador",
    "convenio",
)
_ALL_HEADERS = (
    *_SUMMARY_HEADERS,
    *_MOTIVO_HEADERS,
    *_DIAGNOSTICO_HEADERS,
    *_PROCEDIMIENTO_HEADERS,
    *_MEDICAMENTO_HEADERS,
    *_IDENTITY_HEADERS,
    "hallazgos",
    "conclusion",
    "conclusión",
    "observaciones",
    "antecedentes",
)
_SECTION_SEPARATOR_PATTERN = re.compile(r"[:\-–—]\s*")
_ADMIN_PATTERN = re.compile(
    r"\b(eps|aseguradora|pagador|convenio|poliza|póliza|afiliaci[oó]n|arl|soat)\b",
    flags=re.IGNORECASE,
)
_ADMIN_SUFFIX_PATTERN = re.compile(
    r"\b(s\.?a\.?|sas|ltda|seguros|comerciales|bolivar|bolívar)\b",
    flags=re.IGNORECASE,
)
_CLINICAL_SIGNAL_PATTERN = re.compile(
    r"\b("
    r"paciente|ingresa|ingreso|consulta|presenta|refiere|evolucion|evolución|"
    r"diagnost|hallaz|proced|quirurg|quirúrg|operator|trauma|fract|dolor|"
    r"manejo|tratamiento|medic|hospital|sintoma|síntoma|se realiza|postoperator"
    r")\b",
    flags=re.IGNORECASE,
)


def _normalize_line(value: str) -> str:
    return re.sub(r"\s+", " ", str(value or "")).strip()


def _normalize_ascii(value: str) -> str:
    normalized = unicodedata.normalize("NFKD", str(value or ""))
    return "".join(char for char in normalized if not unicodedata.combining(char)).lower()


def _token_matches_line(normalized_line: str, token: str) -> bool:
    return (
        normalized_line == token
        or normalized_line.startswith(f"{token}:")
        or normalized_line.startswith(f"{token} -")
    )


def _extract_inline_section_value(line: str, token: str) -> str:
    normalized = _normalize_ascii(line)
    position = normalized.find(token)
    if position < 0:
        return ""
    original_suffix = line[position + len(token) :]
    original_suffix = _SECTION_SEPARATOR_PATTERN.sub("", original_suffix, count=1)
    return _normalize_line(original_suffix)


def _is_header_line(normalized_line: str) -> bool:
    if not normalized_line:
        return False
    return any(_token_matches_line(normalized_line, token) for token in _ALL_HEADERS)


def _collect_unique(values: list[str]) -> list[str]:
    result: list[str] = []
    seen: set[str] = set()
    for raw in values:
        value = _normalize_line(raw)
        if not value:
            continue
        key = value.casefold()
        if key in seen:
            continue
        seen.add(key)
        result.append(value)
    return result


def extract_historia_section_text(raw_text: str, headers: tuple[str, ...], *, max_lines: int = 6) -> str:
    lines = [_normalize_line(line) for line in str(raw_text or "").splitlines()]
    collected: list[str] = []
    in_section = False
    for line in lines:
        normalized = _normalize_ascii(line)
        if any(_token_matches_line(normalized, token) for token in headers):
            in_section = True
            for token in headers:
                if _token_matches_line(normalized, token):
                    inline = _extract_inline_section_value(line, token)
                    if inline:
                        collected.append(inline)
                    break
            continue
        if not in_section:
            continue
        if not line:
            break
        if _is_header_line(normalized):
            break
        collected.append(line)
        if len(collected) >= max_lines:
            break
    return "\n".join(_collect_unique(collected))


def extract_historia_motivo_text(raw_text: str) -> str:
    return extract_historia_section_text(raw_text, _MOTIVO_HEADERS, max_lines=3)


def extract_historia_resumen_text(raw_text: str) -> str:
    return extract_historia_section_text(raw_text, _SUMMARY_HEADERS, max_lines=8)


def extract_historia_diagnosticos_text(raw_text: str) -> str:
    return extract_historia_section_text(raw_text, _DIAGNOSTICO_HEADERS, max_lines=8)


def extract_historia_procedimientos_text(raw_text: str) -> str:
    return extract_historia_section_text(raw_text, _PROCEDIMIENTO_HEADERS, max_lines=8)


def extract_historia_medicamentos_text(raw_text: str) -> str:
    return extract_historia_section_text(raw_text, _MEDICAMENTO_HEADERS, max_lines=8)


def is_valid_historia_resumen(text: str) -> bool:
    normalized = _normalize_line(text)
    if not normalized or normalized.casefold() == "no especificado":
        return False
    word_count = len(normalized.split())
    has_clinical_signal = bool(_CLINICAL_SIGNAL_PATTERN.search(normalized))
    looks_administrative = bool(_ADMIN_PATTERN.search(normalized)) or (
        word_count <= 8 and bool(_ADMIN_SUFFIX_PATTERN.search(normalized))
    )
    if looks_administrative and not has_clinical_signal:
        return False
    if word_count <= 1 and not has_clinical_signal:
        return False
    return not (
        word_count <= 10 and normalized.isupper() and looks_administrative and not has_clinical_signal
    )


def _historia_resumen_quality(text: str) -> int:
    normalized = _normalize_line(text)
    if not is_valid_historia_resumen(normalized):
        return -100
    words = normalized.split()
    word_score = min(len(words), 80)
    punctuation_score = 10 if any(mark in normalized for mark in ".;:") else 0
    casing_score = 10 if normalized != normalized.upper() else 0
    clinical_signal_score = min(30, len(_CLINICAL_SIGNAL_PATTERN.findall(normalized)) * 3)
    return word_score + punctuation_score + casing_score + clinical_signal_score


def resolve_historia_resumen(
    candidate: str | None,
    *,
    raw_text: str = "",
    analisis_html: str = "",
) -> str:
    normalized_candidate = _normalize_line(candidate or "")
    if is_valid_historia_resumen(normalized_candidate):
        return normalized_candidate

    raw_summary = extract_historia_resumen_text(raw_text)
    if is_valid_historia_resumen(raw_summary):
        return _normalize_line(raw_summary)

    html_summary = _normalize_line(extract_historia_resumen_from_html(analisis_html or ""))
    if is_valid_historia_resumen(html_summary):
        return html_summary

    return "No especificado"


def choose_historia_resumen(
    candidate: str | None,
    *,
    detailed_candidate: str = "",
    raw_text: str = "",
    analisis_html: str = "",
) -> str:
    resolved_candidate = resolve_historia_resumen(
        candidate,
        raw_text=raw_text,
        analisis_html=analisis_html,
    )
    resolved_detailed = resolve_historia_resumen(
        detailed_candidate,
        raw_text=raw_text,
        analisis_html=analisis_html,
    )
    candidate_words = len(_normalize_line(resolved_candidate).split())
    detailed_words = len(_normalize_line(resolved_detailed).split())
    if (
        is_valid_historia_resumen(resolved_detailed)
        and detailed_words >= candidate_words + 12
        and (
            _normalize_line(resolved_candidate) == _normalize_line(resolved_candidate).upper()
            or candidate_words <= 35
        )
    ):
        return _normalize_line(resolved_detailed)
    if (
        is_valid_historia_resumen(resolved_detailed)
        and _historia_resumen_quality(resolved_detailed) >= _historia_resumen_quality(resolved_candidate) + 12
    ):
        return _normalize_line(resolved_detailed)
    return _normalize_line(resolved_candidate)
