from __future__ import annotations

from datetime import datetime
from typing import Optional

from sqlalchemy import (
    Boolean,
    DateTime,
    Float,
    ForeignKey,
    Integer,
    String,
    Text,
    UniqueConstraint,
    func,
)
from sqlalchemy.orm import Mapped, mapped_column, relationship

from app.core.database import Base


class Project(Base):
    __tablename__ = "projects"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    name: Mapped[str] = mapped_column(String(255), nullable=False)
    status: Mapped[str] = mapped_column(String(64), default="created")
    current_variant: Mapped[str] = mapped_column(String(255), default="No Variations")
    zip_path: Mapped[Optional[str]] = mapped_column(String(1024), nullable=True)
    extract_path: Mapped[Optional[str]] = mapped_column(String(1024), nullable=True)
    pcb_doc_name: Mapped[Optional[str]] = mapped_column(String(512), nullable=True)
    decimal_number: Mapped[str] = mapped_column(String(255), default="")
    board_name: Mapped[str] = mapped_column(String(255), default="")
    error_message: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
    created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
    updated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
    )

    variants: Mapped[list[Variant]] = relationship(back_populates="project", cascade="all, delete-orphan")
    components: Mapped[list[Component]] = relationship(back_populates="project", cascade="all, delete-orphan")
    project_params: Mapped[list[ProjectParam]] = relationship(
        back_populates="project", cascade="all, delete-orphan"
    )
    pcb_data: Mapped[Optional[PcbData]] = relationship(
        back_populates="project", uselist=False, cascade="all, delete-orphan"
    )
    settings: Mapped[list[ProjectSetting]] = relationship(
        back_populates="project", cascade="all, delete-orphan"
    )
    inscriptions: Mapped[list[TitleInscription]] = relationship(
        back_populates="project", cascade="all, delete-orphan"
    )
    designator_mappings: Mapped[list[DesignatorMapping]] = relationship(
        back_populates="project", cascade="all, delete-orphan"
    )
    llm_messages: Mapped[list[LlmMessage]] = relationship(
        back_populates="project", cascade="all, delete-orphan"
    )


class Variant(Base):
    __tablename__ = "variants"
    __table_args__ = (UniqueConstraint("project_id", "name", name="uq_variant_project_name"),)

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    project_id: Mapped[int] = mapped_column(ForeignKey("projects.id", ondelete="CASCADE"), index=True)
    name: Mapped[str] = mapped_column(String(255), nullable=False)

    project: Mapped[Project] = relationship(back_populates="variants")


class Component(Base):
    __tablename__ = "components"
    __table_args__ = (UniqueConstraint("project_id", "designator", name="uq_component_designator"),)

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    project_id: Mapped[int] = mapped_column(ForeignKey("projects.id", ondelete="CASCADE"), index=True)
    designator: Mapped[str] = mapped_column(String(128), nullable=False)

    project: Mapped[Project] = relationship(back_populates="components")
    properties: Mapped[list[ComponentProperty]] = relationship(
        back_populates="component", cascade="all, delete-orphan"
    )
    variant_links: Mapped[list[ComponentVariant]] = relationship(
        back_populates="component", cascade="all, delete-orphan"
    )
    variant_properties: Mapped[list[VariantProperty]] = relationship(
        back_populates="component", cascade="all, delete-orphan"
    )


class ComponentProperty(Base):
    __tablename__ = "component_properties"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    component_id: Mapped[int] = mapped_column(ForeignKey("components.id", ondelete="CASCADE"), index=True)
    key: Mapped[str] = mapped_column(String(255), nullable=False)
    value: Mapped[str] = mapped_column(Text, default="")

    component: Mapped[Component] = relationship(back_populates="properties")


class ComponentVariant(Base):
    __tablename__ = "component_variants"
    __table_args__ = (
        UniqueConstraint("component_id", "variant_id", name="uq_component_variant"),
    )

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    component_id: Mapped[int] = mapped_column(ForeignKey("components.id", ondelete="CASCADE"), index=True)
    variant_id: Mapped[int] = mapped_column(ForeignKey("variants.id", ondelete="CASCADE"), index=True)
    is_fitted: Mapped[bool] = mapped_column(Boolean, default=True)

    component: Mapped[Component] = relationship(back_populates="variant_links")
    variant: Mapped[Variant] = relationship()


class VariantProperty(Base):
    __tablename__ = "variant_properties"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    component_id: Mapped[int] = mapped_column(ForeignKey("components.id", ondelete="CASCADE"), index=True)
    variant_id: Mapped[int] = mapped_column(ForeignKey("variants.id", ondelete="CASCADE"), index=True)
    key: Mapped[str] = mapped_column(String(255), nullable=False)
    value: Mapped[str] = mapped_column(Text, default="")

    component: Mapped[Component] = relationship(back_populates="variant_properties")
    variant: Mapped[Variant] = relationship()


class ProjectParam(Base):
    __tablename__ = "project_params"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    project_id: Mapped[int] = mapped_column(ForeignKey("projects.id", ondelete="CASCADE"), index=True)
    name: Mapped[str] = mapped_column(String(255), nullable=False)
    value: Mapped[str] = mapped_column(Text, default="")
    variant_name: Mapped[str] = mapped_column(String(255), default="")

    project: Mapped[Project] = relationship(back_populates="project_params")


class ProjectSetting(Base):
    __tablename__ = "project_settings"
    __table_args__ = (UniqueConstraint("project_id", "key", name="uq_project_setting"),)

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    project_id: Mapped[int] = mapped_column(ForeignKey("projects.id", ondelete="CASCADE"), index=True)
    key: Mapped[str] = mapped_column(String(255), nullable=False)
    value: Mapped[str] = mapped_column(Text, default="")

    project: Mapped[Project] = relationship(back_populates="settings")


class DesignatorMapping(Base):
    __tablename__ = "designator_mappings"
    __table_args__ = (UniqueConstraint("project_id", "prefix", name="uq_designator_prefix"),)

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    project_id: Mapped[int] = mapped_column(ForeignKey("projects.id", ondelete="CASCADE"), index=True)
    prefix: Mapped[str] = mapped_column(String(16), nullable=False)
    singular_name: Mapped[str] = mapped_column(String(255), default="")
    plural_name: Mapped[str] = mapped_column(String(255), default="")

    project: Mapped[Project] = relationship(back_populates="designator_mappings")


class TitleInscription(Base):
    __tablename__ = "title_inscriptions"
    __table_args__ = (UniqueConstraint("project_id", "field_number", name="uq_inscription_field"),)

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    project_id: Mapped[int] = mapped_column(ForeignKey("projects.id", ondelete="CASCADE"), index=True)
    field_number: Mapped[int] = mapped_column(Integer, nullable=False)
    field_value: Mapped[str] = mapped_column(Text, default="")

    project: Mapped[Project] = relationship(back_populates="inscriptions")


class PcbData(Base):
    __tablename__ = "pcb_data"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    project_id: Mapped[int] = mapped_column(
        ForeignKey("projects.id", ondelete="CASCADE"), unique=True, index=True
    )
    layer_count: Mapped[int] = mapped_column(Integer, default=0)

    project: Mapped[Project] = relationship(back_populates="pcb_data")
    diel_materials: Mapped[list[DielMaterial]] = relationship(
        back_populates="pcb_data", cascade="all, delete-orphan"
    )


class DielMaterial(Base):
    __tablename__ = "diel_materials"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    pcb_data_id: Mapped[int] = mapped_column(ForeignKey("pcb_data.id", ondelete="CASCADE"), index=True)
    name: Mapped[str] = mapped_column(String(255), default="DielMaterial")
    value: Mapped[str] = mapped_column(String(255), default="")
    height: Mapped[float] = mapped_column(Float, default=0.0)
    diel_type: Mapped[int] = mapped_column(Integer, default=1)
    layer_number: Mapped[int] = mapped_column(Integer, default=0)

    pcb_data: Mapped[PcbData] = relationship(back_populates="diel_materials")


class PerechenRow(Base):
    __tablename__ = "perechen_rows"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    project_id: Mapped[int] = mapped_column(ForeignKey("projects.id", ondelete="CASCADE"), index=True)
    row_index: Mapped[int] = mapped_column(Integer, default=0)
    page_number: Mapped[int] = mapped_column(Integer, default=1)
    position: Mapped[str] = mapped_column(Text, default="")
    designation: Mapped[str] = mapped_column(Text, default="")
    quantity: Mapped[str] = mapped_column(String(64), default="")
    note: Mapped[str] = mapped_column(Text, default="")
    is_header: Mapped[bool] = mapped_column(Boolean, default=False)
    is_empty: Mapped[bool] = mapped_column(Boolean, default=False)
    is_auto_generated: Mapped[bool] = mapped_column(Boolean, default=True)
    stretch: Mapped[bool] = mapped_column(Boolean, default=False)
    is_underline: Mapped[bool] = mapped_column(Boolean, default=False)


class SpecificationPcbRow(Base):
    __tablename__ = "specification_pcb_rows"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    project_id: Mapped[int] = mapped_column(ForeignKey("projects.id", ondelete="CASCADE"), index=True)
    row_index: Mapped[int] = mapped_column(Integer, default=0)
    page_number: Mapped[int] = mapped_column(Integer, default=1)
    format: Mapped[str] = mapped_column(String(64), default="")
    zone: Mapped[str] = mapped_column(String(64), default="")
    position: Mapped[str] = mapped_column(String(64), default="")
    designation: Mapped[str] = mapped_column(Text, default="")
    name: Mapped[str] = mapped_column(Text, default="")
    quantity: Mapped[str] = mapped_column(String(64), default="")
    note: Mapped[str] = mapped_column(Text, default="")
    is_header: Mapped[bool] = mapped_column(Boolean, default=False)
    is_empty: Mapped[bool] = mapped_column(Boolean, default=False)
    is_auto_generated: Mapped[bool] = mapped_column(Boolean, default=True)
    stretch: Mapped[bool] = mapped_column(Boolean, default=False)
    is_underline: Mapped[bool] = mapped_column(Boolean, default=False)


class SpecificationRow(Base):
    __tablename__ = "specification_rows"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    project_id: Mapped[int] = mapped_column(ForeignKey("projects.id", ondelete="CASCADE"), index=True)
    row_index: Mapped[int] = mapped_column(Integer, default=0)
    page_number: Mapped[int] = mapped_column(Integer, default=1)
    format: Mapped[str] = mapped_column(String(64), default="")
    zone: Mapped[str] = mapped_column(String(64), default="")
    position: Mapped[str] = mapped_column(String(64), default="")
    designation: Mapped[str] = mapped_column(Text, default="")
    name: Mapped[str] = mapped_column(Text, default="")
    quantity: Mapped[str] = mapped_column(String(64), default="")
    note: Mapped[str] = mapped_column(Text, default="")
    is_header: Mapped[bool] = mapped_column(Boolean, default=False)
    is_empty: Mapped[bool] = mapped_column(Boolean, default=False)
    is_auto_generated: Mapped[bool] = mapped_column(Boolean, default=True)
    stretch: Mapped[bool] = mapped_column(Boolean, default=False)
    is_underline: Mapped[bool] = mapped_column(Boolean, default=False)


class VedomostRow(Base):
    __tablename__ = "vedomost_rows"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    project_id: Mapped[int] = mapped_column(ForeignKey("projects.id", ondelete="CASCADE"), index=True)
    row_index: Mapped[int] = mapped_column(Integer, default=0)
    page_number: Mapped[int] = mapped_column(Integer, default=1)
    name: Mapped[str] = mapped_column(Text, default="")
    product_code: Mapped[str] = mapped_column(Text, default="")
    document_code: Mapped[str] = mapped_column(Text, default="")
    supplier: Mapped[str] = mapped_column(Text, default="")
    where_used: Mapped[str] = mapped_column(Text, default="")
    quantity_per_item: Mapped[str] = mapped_column(String(64), default="")
    quantity_in_set: Mapped[str] = mapped_column(String(64), default="")
    quantity_for_reg: Mapped[str] = mapped_column(String(64), default="")
    total_quantity: Mapped[str] = mapped_column(String(64), default="")
    note: Mapped[str] = mapped_column(Text, default="")
    is_header: Mapped[bool] = mapped_column(Boolean, default=False)
    is_empty: Mapped[bool] = mapped_column(Boolean, default=False)
    is_auto_generated: Mapped[bool] = mapped_column(Boolean, default=True)
    stretch: Mapped[bool] = mapped_column(Boolean, default=False)
    is_underline: Mapped[bool] = mapped_column(Boolean, default=False)


class SimpleListRow(Base):
    __tablename__ = "simple_list_rows"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    project_id: Mapped[int] = mapped_column(ForeignKey("projects.id", ondelete="CASCADE"), index=True)
    row_index: Mapped[int] = mapped_column(Integer, default=0)
    designator: Mapped[str] = mapped_column(Text, default="")
    name: Mapped[str] = mapped_column(Text, default="")
    quantity: Mapped[str] = mapped_column(String(64), default="")
    is_auto_generated: Mapped[bool] = mapped_column(Boolean, default=True)
    is_empty: Mapped[bool] = mapped_column(Boolean, default=False)


class LlmMessage(Base):
    __tablename__ = "llm_messages"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    project_id: Mapped[int] = mapped_column(ForeignKey("projects.id", ondelete="CASCADE"), index=True)
    role: Mapped[str] = mapped_column(String(32), nullable=False)
    content: Mapped[str] = mapped_column(Text, default="")
    table_type: Mapped[Optional[str]] = mapped_column(String(64), nullable=True)
    proposed_edits: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
    created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())

    project: Mapped[Project] = relationship(back_populates="llm_messages")
