"""
Schemas for Items and related entities
Complete nested one-to-one structure:
LineItem -> LineFinancial -> LineQuantity -> LineCustoms -> LineDescription -> LineReference
"""

from typing import Any, Optional, Union
from datetime import datetime
from decimal import Decimal
from pydantic import BaseModel, Field, ConfigDict, model_validator

# Import schemas from individual modules
# Import nested schemas
from .line_customs.schemas import (
    LineCustomCreate,
    LineCustomUpdate,
    LineCustomResponse,
)
from .line_descriptions.schemas import (
    LineDescriptionCreate,
    LineDescriptionUpdate,
    LineDescriptionResponse,
)
from .line_quantities.schemas import (
    LineQuantityCreate,
    LineQuantityUpdate,
    LineQuantityResponse,
)
from .line_financials.schemas import (
    LineFinancialCreate,
    LineFinancialUpdate,
    LineFinancialResponse,
)
from .line_references.schemas import (
    LineReferenceCreate,
    LineReferenceUpdate,
    LineReferenceResponse,
)

from api.v1.modules.a76.general_catalogs.identifiers.dto import (
    IdentifierDetailCreate,
    IdentifierDetailUpdate,
    IdentifierDetailResponse,
)
from api.v1.modules.a76.classes.models import Class
from api.v1.modules.a24.fa.fa_item_lines.dto import (
    FaLineItemCreateDTO,
    FaLineItemUpdateDTO,
    FaLineItemResponseDTO,
)
from .series.schemas import (
    SerieCreate,
    SerieUpdate,
    SerieResponse,
)


# ============================================================================
# ITEM SCHEMAS
# ============================================================================


class LineItemBase(BaseModel):
    """Base schema for items"""

    model_config = ConfigDict(populate_by_name=True)

    invoice_id: int = Field(..., description="Invoice ID")
    line_number: int = Field(..., description="Line number")

    # Part identification
    part_number_id: Union[int, str, None] = Field(
        None,
        description="Part number",
        alias="part_number",
        serialization_alias="part_number_id",
    )
    component_part_number_id: Union[int, str, None] = Field(
        None,
        description="Component part number",
        alias="component_part_number",
        serialization_alias="component_part_number_id",
    )
    class_id: Optional[int] = Field(None, description="Class code")

    # Unit of measure
    unit_of_measure: Optional[int] = Field(None, description="Unit of measure")
    alternate_unit: Optional[int] = Field(None, description="Alternate unit")
    uma_key: Optional[str] = Field(None, max_length=2, description="UMA key")
    auxiliary_unit: Optional[str] = Field(
        None, max_length=5, description="Auxiliary unit"
    )

    # Permits and certificates
    permit_number: Optional[str] = Field(
        None, max_length=20, description="Permit number"
    )
    page_line: Optional[str] = Field(None, max_length=10, description="Page line")
    has_certificate: Optional[bool] = Field(None, description="Has certificate")
    certificate_number: Optional[str] = Field(
        None, max_length=10, description="Certificate number"
    )
    octave_permit: Optional[str] = Field(
        None, max_length=20, description="Octave permit"
    )
    permits_ped: Optional[str] = Field(None, max_length=500, description="PED permits")

    # FDA
    has_fda_code: Optional[bool] = Field(None, description="Has FDA code")
    fda_key: Optional[str] = Field(None, max_length=10, description="FDA key")

    # Special flags
    is_military_mcia: Optional[bool] = Field(
        None, description="Is military merchandise"
    )

    # IV32
    iv32_type_key: Optional[str] = Field(
        None, max_length=5, description="IV32 type key"
    )
    iv32_number: Optional[str] = Field(None, max_length=35, description="IV32 number")

    # Export specific
    scrap_invoice: Optional[str] = Field(
        None, max_length=15, description="Scrap invoice"
    )
    consecutive_destination: Optional[int] = Field(
        None, description="Consecutive destination"
    )
    ctm_section: Optional[str] = Field(None, max_length=3, description="CTM section")

    # Tax payment
    tax_payment: Optional[bool] = Field(None, description="Tax payment")
    payment_method: Optional[str] = Field(
        None, max_length=9, description="Payment method"
    )
    igi_amount: Optional[Decimal] = Field(None, description="IGI amount")
    igi_payment_method: Optional[str] = Field(
        None, max_length=9, description="IGI payment method"
    )

    # FCC
    fcc_key: Optional[str] = Field(None, max_length=30, description="FCC key")

    # Valuation method
    valuation_method: Optional[str] = Field(
        None, max_length=2, description="Valuation method"
    )
    valuation_determined_value: Optional[Decimal] = Field(
        None, description="Valuation determined value"
    )
    valuation_reason: Optional[str] = Field(
        None, max_length=500, description="Valuation reason"
    )

    # Container rules
    container_rule: Optional[str] = Field(
        None, max_length=50, description="Container rule"
    )
    container_parts_ii: Optional[str] = Field(
        None, max_length=50, description="Container parts II"
    )

    # APHIS
    consecutive_aphis: Optional[int] = Field(None, description="Consecutive APHIS")

    # BOM/Commercial
    bom_version: Optional[int] = Field(None, description="BOM version")
    bill_version: Optional[int] = Field(None, description="Bill version")

    # TLCAN value
    tlcan_value: Optional[Decimal] = Field(None, description="TLCAN value")

    # Identifier
    identifier: Optional[str] = Field(None, max_length=2, description="Identifier")

    # Validation fields
    validation_zero: Optional[int] = Field(None, description="Validation zero")
    validation_one: Optional[int] = Field(None, description="Validation one")

    # Material type
    material_type: Optional[str] = Field(
        None, max_length=50, description="Material type"
    )

    # Order concept
    order_type: Optional[str] = Field(None, max_length=50, description="Order type")
    line_concept: Optional[str] = Field(None, max_length=50, description="Line concept")

    # Review dispatch
    review_dispatch: Optional[str] = Field(
        None, max_length=10, description="Review dispatch"
    )

    # Take component from PT
    take_component_pt: Optional[int] = Field(None, description="Take component from PT")

    # Pallet
    pallet2: Optional[int] = Field(None, description="Pallet 2")

    # Wildcard field
    wildcard_field: Optional[str] = Field(
        None, max_length=100, description="Wildcard field"
    )
    reference_number: Optional[str] = Field(
        None, max_length=20, description="Reference number"
    )
    order: Optional[str] = Field(None, max_length=50, description="Order")
    guide_number: Optional[str] = Field(None, max_length=50, description="Guide number")

    # Dates
    depreciation_date: Optional[datetime] = Field(None, description="Depreciation date")

    # Administrative fields
    rectification: Optional[int] = Field(None, description="Rectification")
    warehouse: Optional[str] = Field(None, max_length=30, description="Warehouse")
    location: Optional[str] = Field(None, max_length=200, description="Location")


class LineItemCreate(LineItemBase):
    """Schema for creating item with nested lines (one-to-many)"""

    # Override base fields - estos se asignan automáticamente en el service
    line_number: Optional[int] = Field(None, description="Line number (auto-assigned)")

    financial: Optional[LineFinancialCreate] = Field(
        None, description="Financial data for this line"
    )
    quantity: Optional[LineQuantityCreate] = Field(
        None, description="Quantity data for this line"
    )
    customs: Optional[LineCustomCreate] = Field(
        None, description="Customs data for this line"
    )
    description: Optional[LineDescriptionCreate] = Field(
        None, description="Description data for this line"
    )
    reference: Optional[LineReferenceCreate] = Field(
        None, description="Reference data for this line"
    )
    fa_data: Optional[FaLineItemCreateDTO] = Field(
        None, description="Fixed Asset data for this line"
    )
    series: Optional[list[SerieCreate]] = Field(
        None, description="Series data for this line (multiple per line)"
    )
    identifiers: Optional[list[IdentifierDetailCreate]] = Field(
        None, description="Identifiers for this line"
    )


class LineItemUpdate(LineItemBase):
    """Schema for updating item"""

    invoice_id: Optional[int] = Field(None, description="Invoice ID")
    # Override base fields - todos opcionales en updates
    line_number: Optional[int] = Field(None, description="Line number")
    financial: Optional[LineFinancialUpdate] = Field(
        None, description="Financial data for this line"
    )
    quantity: Optional[LineQuantityUpdate] = Field(
        None, description="Quantity data for this line"
    )
    customs: Optional[LineCustomUpdate] = Field(
        None, description="Customs data for this line"
    )
    description: Optional[LineDescriptionUpdate] = Field(
        None, description="Description data for this line"
    )
    reference: Optional[LineReferenceUpdate] = Field(
        None, description="Reference data for this line"
    )
    fa_data: Optional[FaLineItemUpdateDTO] = Field(
        None, description="Fixed Asset data for this line"
    )
    series: Optional[list[SerieUpdate]] = Field(
        None, description="Series data for this line (replace all)"
    )
    identifiers: Optional[list[IdentifierDetailUpdate]] = Field(
        None, description="Identifiers for this line"
    )


class LineItemResponse(LineItemBase):
    """Schema for single item response"""

    id: int
    invoice_id: int
    line_number: int

    # Part identification
    part_number_id: Optional[int] = Field(
        None, alias="part_number_id_input", serialization_alias="part_number_id"
    )
    part_number: Optional[str] = None
    component_part_number_id: Optional[int] = Field(
        None,
        alias="component_part_number_id_input",
        serialization_alias="component_part_number_id",
    )
    component_part_number: Optional[str] = None
    class_id: Optional[int] = None

    # Nested data
    financial: Optional[LineFinancialResponse] = None
    quantity: Optional[LineQuantityResponse] = None
    customs: Optional[LineCustomResponse] = None
    description: Optional[LineDescriptionResponse] = None
    reference: Optional[LineReferenceResponse] = None
    fa_data: Optional[FaLineItemResponseDTO] = None
    series: Optional[list[SerieResponse]] = None
    identifiers: Optional[list[IdentifierDetailResponse]] = None

    # Fields populated from relationships
    class_code: Optional[str] = None
    class_description: Optional[str] = None
    unit_of_measure_code: Optional[str] = None

    # Additional fields that might be present
    reference_number: Optional[str] = None
    order: Optional[str] = None
    warehouse: Optional[str] = None
    location: Optional[str] = None

    model_config = ConfigDict(from_attributes=True, populate_by_name=True)

    @model_validator(mode="before")
    @classmethod
    def extract_relationship_info(cls, data: Any) -> Any:
        """Extract information from joined relationships to provide flat mapping for UI."""
        if isinstance(data, dict):
            # If already a dict, ensure description syncs to top-level if missing
            desc = data.get("description", {})
            if isinstance(desc, dict):
                if not data.get("part_description_es"):
                    data["part_description_es"] = desc.get("description_spanish")
                if not data.get("part_description_en"):
                    data["part_description_en"] = desc.get("description_english")
            return data

        # It's an ORM object
        result = {}
        
        # 1. Start with model attributes (columns)
        if hasattr(data, "__table__"):
            for k in data.__table__.columns.keys():
                result[k] = getattr(data, k, None)
        else:
            # Fallback for non-table objects if any
            for k, v in data.__dict__.items():
                if not k.startswith("_"):
                    result[k] = v

        # Alias mapping for part numbers
        if hasattr(data, "part_number_id") and "part_number_id" not in result:
            result["part_number_id"] = data.part_number_id
        if hasattr(data, "component_part_number_id") and "component_part_number_id" not in result:
            result["component_part_number_id"] = data.component_part_number_id

        # Extract part info (string part numbers) from relationship objects
        if hasattr(data, "part_info") and data.part_info is not None:
            result["part_number"] = getattr(data.part_info, "part_number", None)
        if hasattr(data, "component_part_info") and data.component_part_info is not None:
            result["component_part_number"] = getattr(data.component_part_info, "part_number", None)

        # Extract class info
        if hasattr(data, "class_info") and data.class_info is not None:
            result["class_code"] = getattr(data.class_info, "class_code", None)
            result["class_description"] = getattr(data.class_info, "description_es", None)

        # Extract unit of measure code
        if hasattr(data, "unit_of_measure_info") and data.unit_of_measure_info is not None:
            result["unit_of_measure_code"] = getattr(data.unit_of_measure_info, "code", None)

        # 2. Extract nested objects and populate redundant descriptions
        # We MUST use explicit getattr for relationships to ensure SQLAlchemy loads/uses joined-loaded ones
        for key in ["financial", "quantity", "customs", "description", "reference", "fa_data", "series", "identifiers"]:
            val = getattr(data, key, None)
            if val is not None:
                result[key] = val
                # Sync to top-level for description redundancy (huge boost for UI stability)
                if key == "description":
                    result["part_description_es"] = getattr(val, "description_spanish", None)
                    result["part_description_en"] = getattr(val, "description_english", None)
            else:
                # Provide default empty dict for core containers to help frontend
                if key in ["financial", "quantity", "customs", "description"]:
                    result[key] = {}

        return result


class LineItemListResponse(BaseModel):
    """Schema for paginated item list"""

    total: int = Field(..., description="Total number of items")
    items: list[LineItemResponse] = Field(..., description="List of items")
    skip: int = Field(..., description="Number of skipped items")
    limit: int = Field(..., description="Maximum items per page")

    model_config = ConfigDict(from_attributes=True)
