"""
Normalized Database Schema for SCAF (Fixed Assets) and SCAII (Parts Inventory)
SQLAlchemy v2 - Annex 24 Compliance
"""

from datetime import datetime
from typing import Optional, List, TYPE_CHECKING
from core.database import Base
from decimal import Decimal
from sqlalchemy import Boolean, Date, String, Integer, Numeric, SmallInteger, ForeignKey
from sqlalchemy.orm import Mapped, mapped_column, relationship
from api.v1.common.base_models import TenantScopedMixin, TimestampMixin
from core.database import Base

from api.v1.modules.a76.classes.models import Class
from api.v1.modules.a76.general_catalogs.units_of_measure.models import UnitOfMeasure

if TYPE_CHECKING:
    from .line_financials.models import LineFinancial
    from .line_quantities.models import LineQuantity
    from .line_customs.models import LineCustom
    from .line_descriptions.models import LineDescription
    from .line_references.models import LineReference
    from api.v1.modules.a76.invoices.models import InvoiceHeader


# ============================================================================
# CORE ENTITIES
# ============================================================================


class LineItem(Base, TenantScopedMixin, TimestampMixin):
    """
    Unified item header table for all import/export operations
    Consolidates headers from both SCAF and SCAII systems
    """

    __tablename__ = "item_lines"
    __table_args__ = {
        "schema": "a76",
    }

    id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
    invoice_id: Mapped[int] = mapped_column(
        ForeignKey("a76.invoice_header.id")
    )  # CONSECUTIVO

    line_number: Mapped[int] = mapped_column(Integer)  # LINEAIMPO/LINEAEXPO/LINEA

    # Part identification
    part_number_id: Mapped[Optional[int]] = mapped_column(
        Integer, ForeignKey("a76.parts.id")
    )  # NUMPARTE
    component_part_number_id: Mapped[Optional[int]] = mapped_column(
        Integer, ForeignKey("a76.parts.id")
    )  # NUMPARTECOM
    class_id: Mapped[Optional[int]] = mapped_column(
        ForeignKey("a76.classes.id")
    )  # CLASE

    # Unit of measure
    unit_of_measure: Mapped[Optional[int]] = mapped_column(
        ForeignKey("a76.units_of_measure.id")
    )  # UNIDADMEDIDA/UNIMED
    alternate_unit: Mapped[Optional[int]] = mapped_column(
        ForeignKey("a76.units_of_measure.id")
    )  # UNIMEDALTERNA
    uma_key: Mapped[Optional[str]] = mapped_column(String(2))  # CLAVEUMA
    auxiliary_unit: Mapped[Optional[str]] = mapped_column(String(5))  # UNIMEDAUXILIAR

    # Permits and certificates
    permit_number: Mapped[Optional[str]] = mapped_column(String(20))  # NUMPERMISO
    page_line: Mapped[Optional[str]] = mapped_column(String(10))  # PAGRENGLON
    has_certificate: Mapped[Optional[bool]] = mapped_column(
        Boolean
    )  # TIENECO/CERTORIGEN
    certificate_number: Mapped[Optional[str]] = mapped_column(
        String(10)
    )  # NOCERTIFICADO
    octave_permit: Mapped[Optional[str]] = mapped_column(String(20))  # PERMISOROCTAVA
    permits_ped: Mapped[Optional[str]] = mapped_column(String(500))  # PERMISOSPED

    # FDA
    has_fda_code: Mapped[Optional[bool]] = mapped_column(Boolean)  # LLEVACODFDA
    fda_key: Mapped[Optional[str]] = mapped_column(String(10))  # CLAVEFDA

    # Special flags
    is_military_mcia: Mapped[Optional[bool]] = mapped_column(Boolean)  # ESMCIAMILITAR

    # IV32 (Tax identification)
    iv32_type_key: Mapped[Optional[str]] = mapped_column(String(5))  # CLAVETIPOIV32
    iv32_number: Mapped[Optional[str]] = mapped_column(String(35))  # NUMEROIV32

    # IN CASE OF EXPO
    scrap_invoice: Mapped[Optional[str]] = mapped_column(String(15))  # FACTURASCRAP
    consecutive_destination: Mapped[Optional[int]] = mapped_column(
        Integer
    )  # CONSECUTIVODES
    ctm_section: Mapped[Optional[str]] = mapped_column(String(3))  # APARTADOCTM

    # Tax payment
    tax_payment: Mapped[Optional[bool]] = mapped_column(Boolean)  # PAGOIMPUESTO
    payment_method: Mapped[Optional[str]] = mapped_column(
        String(9)
    )  # FORMAPAGO/FORMAPAGOTIGI
    igi_amount: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8))  # MONTOIGI
    igi_payment_method: Mapped[Optional[str]] = mapped_column(
        String(9)
    )  # FORMAPAGOTIGI

    # FCC
    fcc_key: Mapped[Optional[str]] = mapped_column(String(30))  # CLAVEFCC

    # Valuation method
    valuation_method: Mapped[Optional[str]] = mapped_column(String(2))  # METVALOR
    valuation_determined_value: Mapped[Optional[Decimal]] = mapped_column(
        Numeric(29, 8)
    )  # METVALORVALORDETERMINADO/METVALORACIONVALORDETERMINADO
    valuation_reason: Mapped[Optional[str]] = mapped_column(
        String(500)
    )  # METVALORMOTIVODEUSO/METVALORACIONMOTIVODEUSO

    # Container rules
    container_rule: Mapped[Optional[str]] = mapped_column(String(50))  # CONTENEDORREGLA
    container_parts_ii: Mapped[Optional[str]] = mapped_column(
        String(50)
    )  # CONTENEDORPARTESII

    # APHIS
    consecutive_aphis: Mapped[Optional[int]] = mapped_column(
        Integer
    )  # CONSECUTIVOAPHIS

    # BOM/Commercial
    bom_version: Mapped[Optional[int]] = mapped_column(Integer)  # VERSIONBOM
    bill_version: Mapped[Optional[int]] = mapped_column(Integer)  # VERSIONBILL

    # TLCAN value
    tlcan_value: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8))  # VALORTLCAN

    # Identifier
    identifier: Mapped[Optional[str]] = mapped_column(String(2))  # IDENTIFICADOR

    # Validation fields
    validation_zero: Mapped[Optional[int]] = mapped_column(Integer)  # VALIDACIONZERO
    validation_one: Mapped[Optional[int]] = mapped_column(Integer)  # VALIDACIONUNO

    # Material type
    material_type: Mapped[Optional[str]] = mapped_column(
        String(50)
    )  # TIPOMAT/TIPODENUMPARTE

    # Order concept
    order_type: Mapped[Optional[str]] = mapped_column(String(50))  # TIPODEORDEN
    line_concept: Mapped[Optional[str]] = mapped_column(
        String(50)
    )  # CONCEPTODELAPARTIDA

    # Review dispatch
    review_dispatch: Mapped[Optional[str]] = mapped_column(String(10))  # REVISARDESP

    # Take component from PT
    take_component_pt: Mapped[Optional[int]] = mapped_column(Integer)  # TOMARCOMOPT

    # Pallet
    pallet2: Mapped[Optional[int]] = mapped_column(SmallInteger)  # PALLET2

    # Wildcard field
    wildcard_field: Mapped[Optional[str]] = mapped_column(String(100))  # CAMPOCOMODIN

    # Item references
    reference_number: Mapped[Optional[str]] = mapped_column(String(20))  # NUMREFERENCIA
    order: Mapped[Optional[str]] = mapped_column(String(50))  # ORDENCOMPRA / ORDENVENTA
    guide_number: Mapped[Optional[str]] = mapped_column(
        String(50)
    )  # NUMEROGUIA/NUMERODEGUIA

    # Dates
    depreciation_date: Mapped[Optional[datetime]] = mapped_column(
        Date
    )  # FECHADEPRECIACION

    # Administrative fields
    rectification: Mapped[Optional[bool]] = mapped_column(Boolean)  # RECTIFICACION
    warehouse: Mapped[Optional[str]] = mapped_column(String(30))  # BODEGA
    location: Mapped[Optional[str]] = mapped_column(String(200))  # LOCALIZACION

    invoice: Mapped["InvoiceHeader"] = relationship("InvoiceHeader")

    # Relationships
    financial: Mapped[Optional["LineFinancial"]] = relationship(
        back_populates="line", cascade="all, delete-orphan", uselist=False
    )
    quantity: Mapped[Optional["LineQuantity"]] = relationship(
        back_populates="line", cascade="all, delete-orphan", uselist=False
    )
    customs: Mapped[Optional["LineCustom"]] = relationship(
        back_populates="line", cascade="all, delete-orphan", uselist=False
    )
    description: Mapped[Optional["LineDescription"]] = relationship(
        back_populates="line", cascade="all, delete-orphan", uselist=False
    )
    reference: Mapped[Optional["LineReference"]] = relationship(
        back_populates="line", cascade="all, delete-orphan", uselist=False
    )
    class_info: Mapped[Optional["Class"]] = relationship(
        "api.v1.modules.a76.classes.models.Class",
        foreign_keys=[class_id],
        viewonly=True,
    )
    unit_of_measure_info: Mapped[Optional["UnitOfMeasure"]] = relationship(
        "api.v1.modules.a76.general_catalogs.units_of_measure.models.UnitOfMeasure",
        foreign_keys=[unit_of_measure],
        viewonly=True,
    )
    fa_data: Mapped[Optional["FaLineItem"]] = relationship(
        "FaLineItem",
        cascade="all, delete-orphan",
        uselist=False,
    )
    identifiers: Mapped[List["IdentifierDetail"]] = relationship(
        "IdentifierDetail",
        back_populates="line",
        cascade="all, delete-orphan",
    )
    series: Mapped[List["Serie"]] = relationship(
        "Serie",
        cascade="all, delete-orphan",
    )

    part_info: Mapped[Optional["Part"]] = relationship(
        "Part",
        foreign_keys=[part_number_id],
        viewonly=True,
    )
    component_part_info: Mapped[Optional["Part"]] = relationship(
        "Part",
        foreign_keys=[component_part_number_id],
        viewonly=True,
    )


# ============================================================================
# SUPPORTING TABLES
# ============================================================================


class PackingList(Base, TenantScopedMixin, TimestampMixin):
    """
    Packing list items
    From: SPartidasPackingList
    """

    __tablename__ = "packing_lists"
    __table_args__ = {
        "schema": "a76",
    }

    id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
    item_line_id: Mapped[int] = mapped_column(Integer)  # LINEA
    packing_list_number: Mapped[Optional[str]] = mapped_column(
        String(100)
    )  # NUMPACKINGLIST


class CTMReceipt(Base, TenantScopedMixin, TimestampMixin):
    """
    CTM Receipt lines (temporary manufacturing)
    From: SPartidasReciboCTM
    """

    __tablename__ = "ctm_receipts"
    __table_args__ = {
        "schema": "a76",
    }

    id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
    receipt_line: Mapped[int] = mapped_column(
        ForeignKey("a76.item_lines.id")
    )  # LINEARECIBO

    option: Mapped[Optional[str]] = mapped_column(String(3))  # OPCION
    exit_invoice: Mapped[Optional[str]] = mapped_column(String(19))  # FACTURASALIDA


class SubassemblyEntry(Base, TenantScopedMixin, TimestampMixin):
    """
    Subassembly/Submanufacturing Entry lines
    From: SPartidasEntradaSM
    """

    __tablename__ = "subassembly_entries"
    __table_args__ = {
        "schema": "a76",
    }

    id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
    remission_line: Mapped[int] = mapped_column(Integer)  # LINEAREMISION
    exit_invoice: Mapped[Optional[str]] = mapped_column(String(15))  # FACTURASALIDA
    exit_line: Mapped[Optional[int]] = mapped_column(Integer)  # LINEASALIDA


# ============================================================================
# INDEXES AND CONSTRAINTS
# ============================================================================


"""
Recommended indexes for optimal query performance:

CREATE INDEX idx_items_consecutive ON items(consecutive);
CREATE INDEX idx_items_invoice ON items(invoice_number);
CREATE INDEX idx_items_type_system ON items(item_type, system_origin);
CREATE INDEX idx_items_dates ON items(invoice_date, depreciation_date);

CREATE INDEX idx_lines_item ON item_lines(item_id);
CREATE INDEX idx_lines_part ON item_lines(part_number);
CREATE INDEX idx_lines_class ON item_lines(class_code);
CREATE INDEX idx_lines_invoice_refs ON item_lines(import_invoice, export_invoice);
CREATE INDEX idx_lines_fractions ON item_lines(import_fraction, export_fraction);

CREATE INDEX idx_packing_consecutive ON packing_lists(consecutive);
CREATE INDEX idx_packing_part ON packing_lists(part_number);

CREATE INDEX idx_repair_invoice ON repair_parts(import_invoice, import_line);
CREATE INDEX idx_ctm_ship_consec ON ctm_shipments(consecutive);
CREATE INDEX idx_ctm_rcpt_consec ON ctm_receipts(consecutive);
CREATE INDEX idx_sub_entry_consec ON subassembly_entries(consecutive);
CREATE INDEX idx_sub_exit_consec ON subassembly_exits(consecutive);
CREATE INDEX idx_imposition_consec ON imposition_parts(consecutive);
"""


# ============================================================================
# MIGRATION NOTES
# ============================================================================

"""
MIGRATION STRATEGY FROM ORIGINAL TABLES TO NORMALIZED SCHEMA:

1. DOCUMENT MAPPING:
   - QEqeMaq (Equipment Import Temp) → items (type='EQUIPMENT_IMPORT_TEMP', system='SCAF')
   - QEqeMaqRep (Equipment Repair Export) → items (type='EQUIPMENT_REPAIR_EXPORT', system='SCAF')
   - QEqiDef (Equipment Import Definitive) → items (type='EQUIPMENT_IMPORT_DEF', system='SCAF')
   - QEqiMaq (Equipment Machinery) → items (type='EQUIPMENT_MACHINERY', system='SCAF')
   - QEqiMaqRep (Equipment Machinery Repair) → items (type='EQUIPMENT_REPAIR_IMPORT', system='SCAF')
   - SPartidasCM (Common Commerce) → items (type='COMMON_COMMERCE', system='SCAII')
   - SPartidasExpo (Export) → items (type='EXPORT', system='SCAII')
   - SPartidasImpo (Import) → items (type='IMPORT', system='SCAII')

2. LINE MAPPING:
   All line items from Q* and SPartidas* tables map to item_lines with appropriate
   field mapping based on the original column names (preserved as comments).

3. FIELD CONSOLIDATION RULES:
   - Costs: Unified under unit_cost_* with currency suffix (usd/mxn/mc)
   - Values: Unified under value_* with currency suffix
   - Quantities: Unified under quantity_* with specific purpose suffixes
   - Descriptions: Consolidated into description_spanish/english/extra
   - Fractions: All fraction fields preserved with clear naming
   
4. DATA INTEGRITY:
   - Original CONSECUTIVO + LINE number preserved for traceability
   - Foreign key relationships established via item_id
   - All original fields retained to prevent data loss
   
5. SPECIAL TABLES:
   - PackingList, RepairPart, CTM*, Subassembly*, ImpositionPart remain separate
     as they serve specific purposes and don't fit the main item/line pattern

6. BENEFITS:
   - Eliminates redundancy across 13 original tables
   - Unified query interface for all operations
   - Maintains full audit trail with original field names
   - Enables cross-system reporting (SCAF + SCAII)
   - Simplifies maintenance with single schema
   
7. QUERYING EXAMPLES:
   ```python
   # Get all imports (both systems)
   session.query(LineItem).filter(
       LineItem.item_type.in_(['IMPORT', 'EQUIPMENT_IMPORT_TEMP', 'EQUIPMENT_IMPORT_DEF'])
   )
   
   # Get all lines for a specific part across all items
   session.query(LineItem).filter(
       LineItem.part_number == 'ABC123'
   )
   
   # Get SCAF equipment with depreciation
   session.query(LineItem).join(LineItem).filter(
       LineItem.system_origin == 'SCAF',
       LineItem.value_depreciated_usd.isnot(None)
   )
   ```
"""
# ============================================================================
# RUNTIME IMPORTS FOR MAPPER RESOLUTION
# ============================================================================
# We import these specialized models at the bottom to ensure they are registered
# in the SQLAlchemy metadata for relationship resolution while avoiding 
# circular import issues in the module head.

from api.v1.modules.a24.fa.fa_item_lines.models import FaLineItem
from api.v1.modules.a76.parts.models import Part
from api.v1.modules.a76.items.series.models import Serie
from api.v1.modules.a76.general_catalogs.identifiers.models import IdentifierDetail
