"""Extração de domínios a partir de PDF, XML, CSV e TXT.

Usado pela importação em lote do painel para ler ofícios e planilhas
enviados pela Anatel / Poder Judiciário.

Desenvolvedor: Euquias Junior <junior@jbinfo.com.br>
"""
from __future__ import annotations

import io
import re
import xml.etree.ElementTree as ET

from .rpz import normalize_domain

SUPPORTED_EXTENSIONS = (".txt", ".csv", ".xml", ".pdf")

# Captura dominios e URLs em texto livre (oficios, planilhas convertidas em PDF)
CANDIDATE_RE = re.compile(
    r"(?:(?:https?://)?(?:www\.)?)"
    r"((?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\.)+[A-Za-z]{2,24})",
)


class ParserError(RuntimeError):
    pass


def _dedupe(values: list[str]) -> list[str]:
    out: list[str] = []
    seen: set[str] = set()
    for value in values:
        domain = normalize_domain(value)
        if domain and domain not in seen:
            seen.add(domain)
            out.append(domain)
    return out


def _from_free_text(text: str) -> list[str]:
    return _dedupe(CANDIDATE_RE.findall(text))


def _from_lines(text: str) -> tuple[list[str], list[str]]:
    """TXT/CSV: primeiro campo de cada linha. Retorna (válidos, inválidos)."""
    valid: list[str] = []
    invalid: list[str] = []
    for raw_line in text.splitlines():
        line = raw_line.strip()
        if not line or line.startswith("#"):
            continue
        first = line.split(";")[0].split(",")[0].split("\t")[0].strip()
        domain = normalize_domain(first)
        if domain:
            valid.append(domain)
        else:
            found = _from_free_text(line)
            if found:
                valid.extend(found)
            else:
                invalid.append(line[:60])
    return _dedupe(valid), invalid


def _from_xml(data: bytes) -> list[str]:
    try:
        root = ET.fromstring(data.decode("utf-8", errors="replace"))
    except ET.ParseError as exc:
        raise ParserError(f"XML inválido: {exc}") from exc
    chunks: list[str] = []
    for element in root.iter():
        if element.text:
            chunks.append(element.text)
        for value in element.attrib.values():
            chunks.append(value)
    return _from_free_text("\n".join(chunks))


def _from_pdf(data: bytes) -> list[str]:
    try:
        from pypdf import PdfReader
    except ImportError as exc:  # pragma: no cover - dependência do servidor
        raise ParserError(
            "Leitura de PDF indisponível: instale a dependência 'pypdf' "
            "(apt install python3-pypdf ou pip install pypdf)."
        ) from exc
    try:
        reader = PdfReader(io.BytesIO(data))
        pages = [page.extract_text() or "" for page in reader.pages]
    except Exception as exc:  # pypdf levanta erros variados
        raise ParserError(f"Não foi possível ler o PDF: {exc}") from exc
    text = "\n".join(pages)
    if not text.strip():
        raise ParserError(
            "O PDF não contém texto extraível (provavelmente é digitalizado). "
            "Converta para TXT/CSV antes de importar."
        )
    return _from_free_text(text)


def extract_domains(filename: str, data: bytes) -> tuple[list[str], list[str]]:
    """Retorna (domínios, linhas_invalidas) conforme a extensão do arquivo."""
    name = (filename or "").lower()
    if name.endswith(".pdf"):
        return _from_pdf(data), []
    if name.endswith(".xml"):
        return _from_xml(data), []
    if name.endswith((".txt", ".csv")):
        return _from_lines(data.decode("utf-8", errors="replace"))
    raise ParserError("Formato não suportado. Use .txt, .csv, .xml ou .pdf.")
