"""Extracción segura de archivos ZIP para la carga masiva."""

from __future__ import annotations

from pathlib import Path
import shutil
import zipfile

from app.batch_processing.domain.models import (
    ArchiveExtractionResult,
    ExtractedArchiveEntry,
    FILE_STATUS_FALLIDO,
)


class SafeZipArchiveExtractor:
    """Descomprime el ZIP evitando path traversal y filtrando archivos no soportados."""

    def __init__(self, working_root: Path) -> None:
        self.working_root = Path(working_root) / "_batch_extracted"
        self.working_root.mkdir(parents=True, exist_ok=True)

    def extract_archive(self, archive_path: str, batch_id: str) -> ArchiveExtractionResult:
        result = ArchiveExtractionResult()
        archive_file = Path(archive_path)
        output_dir = self.working_root / batch_id
        if output_dir.exists():
            shutil.rmtree(output_dir)
        output_dir.mkdir(parents=True, exist_ok=True)

        with zipfile.ZipFile(archive_file) as zf:
            for member in zf.infolist():
                if member.is_dir():
                    continue
                normalized = member.filename.replace("\\", "/").strip()
                # Se rechazan rutas absolutas o intentos de salir del directorio del lote.
                if not normalized or normalized.startswith("/") or ".." in Path(normalized).parts:
                    result.rejected_entries.append(
                        ExtractedArchiveEntry(
                            original_name=Path(normalized or member.filename).name or member.filename,
                            relative_path=member.filename,
                            status=FILE_STATUS_FALLIDO,
                            error="Entrada inválida en ZIP.",
                        )
                    )
                    continue
                if not normalized.lower().endswith(".pdf"):
                    result.rejected_entries.append(
                        ExtractedArchiveEntry(
                            original_name=Path(normalized).name,
                            relative_path=normalized,
                            status=FILE_STATUS_FALLIDO,
                            error="Solo se soportan PDFs dentro del ZIP.",
                        )
                    )
                    continue

                destination = output_dir / normalized
                destination.parent.mkdir(parents=True, exist_ok=True)
                with zf.open(member) as source, destination.open("wb") as target:
                    shutil.copyfileobj(source, target)

                result.entries.append(
                    ExtractedArchiveEntry(
                        original_name=Path(normalized).name,
                        relative_path=normalized,
                        extracted_path=str(destination),
                    )
                )

        return result
