import os
import tempfile
from datetime import date, datetime
from typing import List, Tuple, Optional, Dict, Any
from pathlib import Path

from sqlalchemy.orm import Session, joinedload
from fastapi import HTTPException

from .schemas import (
    Mainx30GenerationRequest, Mainx30Response, ErrorValidacion,
    EmpresaDatos, ConfiguracionSistema
)

# --- MODELOS A76 ---
from api.v1.modules.a76.general_catalogs.company.models import Company as GEmpresa
from api.v1.modules.a76.general_catalogs.ports.models import Port

# --- PROCESADORES ---
from .processors import ScafDefProcessor

class Mainx30DefinitiveService:
    def __init__(self):
        self.errores_validacion: List[ErrorValidacion] = []
        self.cuenta_partidas = 0
        self.cuenta_facturas = 0
        self.valor_total = 0.0
        self.flete_total = 0.0
        self.peso_bruto_total = 0.0
        self.peso_neto_total = 0.0
        
    def generar_mainx30(
        self, 
        db: Session, 
        request: Mainx30GenerationRequest,
        task_instance=None
    ) -> Mainx30Response:
        try:
            self._inicializar_variables()
            
            # 1. Obtener Datos de Empresa
            datos_empresa = self._obtener_datos_company(db)
            emp_dict = datos_empresa.model_dump()
            
            # 2. Obtener Descripciones de Puertos
            if request.entry_port:
                p_ent = db.query(Port).filter(Port.port_code == request.entry_port).first()
                if p_ent: emp_dict['entry_port_desc'] = p_ent.description or p_ent.location_description or ""
            
            if request.exit_port:
                p_sal = db.query(Port).filter(Port.port_code == request.exit_port).first()
                if p_sal: emp_dict['exit_port_desc'] = p_sal.description or p_sal.location_description or ""

            # 3. Fecha de Transmisión (YYMMDD)
            fecha_transmision = datetime.now().strftime("%y%m%d")
            
            # 4. Determinar Procesador (Always ScafDefProcessor for this service)
            processor = ScafDefProcessor()
            # Both 'Definitiva' and 'DEFINITIVO SCAF' use the same heavy processor
            
            # 5. Procesar Facturas
            if not request.facturas:
                 raise HTTPException(status_code=400, detail="No se proporcionaron facturas para procesar.")
                 
            l_facturas, e_facturas = processor.procesar_facturas(db, request.facturas, emp_dict, request)
            
            self.errores_validacion.extend(e_facturas)
            
            # 6. Construir Líneas del Archivo
            lineas = []
            
            # Línea A
            broker = (datos_empresa.broker or "")[:6]
            # Clarion uses Broker twice in 'A' record for Definitive
            ftp_key = (datos_empresa.ftp_key or "00SCSI")[:6]
            lineas.append(f"A {fecha_transmision}03{broker:<6}{broker:<10}{ftp_key:<6}")
            self.cuenta_partidas += 1
            
            # Agregar Líneas de Facturas
            lineas.extend(l_facturas)
            self.cuenta_partidas += processor.cuenta_partidas
            self.cuenta_facturas = processor.cuenta_facturas
            self.valor_total = processor.valor_total_factura
            self.peso_bruto_total = processor.peso_bruto_factura
            self.peso_neto_total = processor.peso_neto_factura
            self.flete_total = processor.flete_total

            # MF80 (Totales Globales)
            val_int = int(round(self.valor_total * 100))
            pb_int = int(round(self.peso_bruto_total * 10000))
            pn_int = int(round(self.peso_neto_total * 10000))
            flete_int = int(round(self.flete_total * 100))
            
            lineas.append(
                f"MF80{val_int:012d}"
                f"{self.cuenta_facturas:04d}"
                f"{pb_int:012d}"
                f"{flete_int:08d}"
                f"{pn_int:012d}"
            )
            self.cuenta_partidas += 1
            
            # Línea Z (Total de líneas)
            lineas.append(f"Z {self.cuenta_partidas:05d}") 
            self.cuenta_partidas += 1
            
            # 7. Generar Nombre y Guardar
            nombre_archivo = f"{request.facturas[0][:15]}_Mainx30.dat"
            if len(request.facturas) > 1:
                nombre_archivo = f"MULTIPLE_Mainx30.dat"
            
            if request.nomenclatura_factura and len(request.facturas) == 1:
                nombre_archivo = f"{request.facturas[0][:15]}_Mainx30.dat"

            content = '\r\n'.join(lineas)
            
            return Mainx30Response(
                success=len(self.errores_validacion) == 0,
                message="Archivo generado" if len(self.errores_validacion) == 0 else "Archivo generado con errores de validación",
                archivo_generado=nombre_archivo,
                ruta_archivo="",
                content=content,
                errores_validacion=self.errores_validacion,
                cuenta_partidas=self.cuenta_partidas,
                valor_total=self.valor_total,
                flete_total=self.flete_total,
                peso_bruto_total=self.peso_bruto_total,
                peso_neto_total=self.peso_neto_total,
                cuenta_facturas=self.cuenta_facturas,
                tiene_inconsistencias=len(self.errores_validacion) > 0
            )
            
        except HTTPException:
            raise
        except Exception as e:
            import traceback
            traceback.print_exc()
            raise HTTPException(status_code=500, detail=f"Error generando Mainx30 Definitivo: {str(e)}")
    
    def _inicializar_variables(self):
        self.errores_validacion = []
        self.cuenta_partidas = 0
        self.valor_total = 0.0
        self.flete_total = 0.0
        self.peso_bruto_total = 0.0
        self.peso_neto_total = 0.0
        self.cuenta_facturas = 0
    
    def _obtener_datos_company(self, db: Session) -> EmpresaDatos:
        empresa = db.query(GEmpresa).options(joinedload(GEmpresa.addresses)).first()
        if not empresa:
            return EmpresaDatos(broker="", responsable="", rfc="", tiene_linea_express="N", nombre_empresa="", manufacturer_id="", ftp_key="", main_activity="", city_state="")
            
        city_state = ""
        main_addr = next((a for a in (empresa.addresses or []) if a.address_type == 'main'), None)
        if not main_addr and empresa.addresses:
            main_addr = empresa.addresses[0]
            
        if main_addr:
            cp = (main_addr.postal_code or "")[:5]
            city = (main_addr.city or "")[:11]
            state = (main_addr.state or "")[:4]
            city_state = f"{cp:<5}{city:<11}{state:<4}"
            
        return EmpresaDatos(
            broker=(empresa.broker_company or "")[:5], 
            responsable=(empresa.responsible or "")[:30],
            rfc=(empresa.rfc or "")[:13],
            tiene_linea_express="N",
            nombre_empresa=(empresa.name or "")[:40],
            manufacturer_id=(empresa.manufacturer_id or "")[:10],
            ftp_key=(empresa.ftp_key or "")[:10],
            main_activity=(empresa.main_activity or "")[:30],
            city_state=city_state[:30]
        )
