"""
Tareas Celery para importación CSV de Exportación (encabezado y partidas).
Para invoice_header: scan_file delega en facturas._do_scan_file (validaciones FK y reporte de errores);
insert_valid_rows delega en facturas._do_insert_valid_rows (inserción en BD). Storage "exp".
"""
import logging
import os
from typing import Dict, Any, Optional

from core.celery_app import celery_app

from ..common import storage as common_storage
from ..common import normalize as common_normalize
from ..common import meta as common_meta
from ..common import responses as common_responses
from ..common import csv_reader as common_csv_reader
from .template_config import row_from_template

logger = logging.getLogger(__name__)

JOB_TYPE = "exp"
EXP_IMPORT_REDIS_TTL = common_storage.IMPORT_REDIS_TTL


def _ensure_file(job_id: str) -> Optional[str]:
    return common_storage.ensure_file_from_redis(JOB_TYPE, job_id, "Exportación import")


def _ensure_meta(job_id: str, file_path: str) -> bool:
    return common_storage.ensure_meta_from_redis(JOB_TYPE, job_id, file_path, "Exportación import")


def _norm_row(row: Dict[str, Any], template_id: str) -> Dict[str, Any]:
    return row_from_template(row, template_id, common_normalize.normalize_header)


@celery_app.task(bind=True, name="api.v1.modules.a76.layouts_csv.exportacion.tasks.scan_file")
def scan_file(self, job_id: str, model_target: str, config: str = None):
    """
    Para invoice_header: delega en facturas._do_scan_file con storage "exp" (validaciones FK, encabezados_expo).
    Para invoice_details: delega en facturas._do_scan_file con template exp_def_partidas (validaciones partidas expo).
    """
    logger.info("Exportación import: starting scan for job %s target %s", job_id, model_target)

    if model_target == "invoice_header":
        from api.v1.modules.a76.layouts_csv.facturas.tasks import _do_scan_file
        return _do_scan_file(self, job_id, "invoice_header", config, job_type_override="exp")

    if model_target == "invoice_details":
        from api.v1.modules.a76.layouts_csv.facturas.tasks import _do_scan_file
        return _do_scan_file(self, job_id, "invoice_details", config, job_type_override="exp")

    if model_target == "invoice_series":
        from api.v1.modules.a76.layouts_csv.facturas.tasks import _do_scan_file
        return _do_scan_file(self, job_id, "invoice_series", config, job_type_override="exp")

    # Fallback (e.g. unknown model_target)
    file_path = _ensure_file(job_id)
    if not file_path:
        return {"status": "failed", "error": "Archivo no encontrado (expirado o no subido). Sube de nuevo."}
    if os.path.getsize(file_path) == 0:
        return {"status": "failed", "error": "El archivo está vacío."}
    _ensure_meta(job_id, file_path)
    try:
        common_meta.require_tenant_context(file_path)
    except ValueError as e:
        return {"status": "failed", "error": str(e)}
    meta = common_meta.load_meta(file_path) or {}
    template_id = meta.get("template_id") or "exp_def_details"
    try:
        total_rows = common_csv_reader.count_csv_rows(file_path, has_header=True)
    except Exception as e:
        return {"status": "failed", "error": str(e)}
    processed_rows = 0
    try:
        for i, row in common_csv_reader.iter_csv_rows(file_path, fieldnames=None):
            self.update_state(state="PROGRESS", meta={"current": i, "total": total_rows})
            _norm_row(row, template_id)
            processed_rows += 1
    except Exception as e:
        logger.error("Exportación import scan failed: %s", e)
        return {"status": "failed", "error": str(e)}
    return common_responses.scan_result(job_id, processed_rows, 0, [])


@celery_app.task(bind=True, name="api.v1.modules.a76.layouts_csv.exportacion.tasks.insert_valid_rows")
def insert_valid_rows(self, job_id: str, model_target: str):
    """
    Commit: para invoice_header delega en facturas (inserción real en BD con storage "exp").
    Para invoice_details delega en facturas (inserción partidas expo cuando esté implementada; mientras tanto mismo flujo).
    """
    logger.info("Exportación import: starting commit for job %s target %s", job_id, model_target)

    if model_target == "invoice_header":
        from api.v1.modules.a76.layouts_csv.facturas.tasks import _do_insert_valid_rows
        return _do_insert_valid_rows(job_id, "invoice_header", job_type_override="exp")

    if model_target == "invoice_details":
        from api.v1.modules.a76.layouts_csv.facturas.tasks import _do_insert_valid_rows
        return _do_insert_valid_rows(job_id, "invoice_details", job_type_override="exp")

    if model_target == "invoice_series":
        from api.v1.modules.a76.layouts_csv.facturas.tasks import _do_insert_valid_rows
        return _do_insert_valid_rows(job_id, "invoice_series", job_type_override="exp")

    # Fallback: stub sin inserción
    file_path = _ensure_file(job_id)
    if not file_path:
        alt_path = common_storage.file_path_for_job(JOB_TYPE, job_id)
        if not os.path.exists(alt_path):
            return {"status": "failed", "error": "Archivo no encontrado (expirado). Sube y confirma de nuevo."}
        file_path = alt_path
    else:
        _ensure_meta(job_id, file_path)

    try:
        common_meta.require_tenant_context(file_path)
    except ValueError as e:
        return {"status": "failed", "error": str(e)}

    meta = common_meta.load_meta(file_path) or {}
    meta_path = common_meta.get_meta_path(file_path)
    error_path = common_storage.error_path_for_job(JOB_TYPE, job_id)
    error_lines = common_storage.get_error_lines(JOB_TYPE, job_id, error_path)

    template_id = meta.get("template_id") or (
        "exp_def_header" if model_target == "invoice_header" else "exp_def_partidas"
    )

    try:
        for i, row in common_csv_reader.iter_csv_rows(file_path, fieldnames=None):
            if i in error_lines:
                continue
            _norm_row(row, template_id)
    except Exception as e:
        logger.error("Exportación import commit read failed: %s", e)
        return {"status": "failed", "error": str(e)}

    common_storage.cleanup_import_job(
        JOB_TYPE, job_id,
        file_path=file_path,
        error_path=error_path,
        meta_path=meta_path,
    )

    return {
        "status": "finished",
        "inserted": 0,
        "skipped_invalid": 0,
        "skipped_missing_fk": 0,
        "skipped_duplicate": 0,
        "skipped_details": [],
        "message": "Proceso base listo; validaciones e inserción pendientes.",
    }
