import uuid
from datetime import datetime

from sqlalchemy import Boolean, DateTime, Float, ForeignKey, Integer, String, Text, UniqueConstraint
from sqlalchemy.dialects.postgresql import JSONB, UUID
from sqlalchemy.orm import Mapped, mapped_column, relationship

from api.models.base import Base, TimestampMixin, new_uuid


class FabricGraphImport(Base, TimestampMixin):
    __tablename__ = "fabric_graph_imports"
    __table_args__ = (
        UniqueConstraint(
            "source_repo_slug",
            "content_hash",
            name="uq_fabric_graph_imports_source_hash",
        ),
    )

    id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True), primary_key=True, default=new_uuid
    )
    source_repo_slug: Mapped[str] = mapped_column(String(100), nullable=False, index=True)
    source_url: Mapped[str | None] = mapped_column(Text, nullable=True)
    source_commit: Mapped[str | None] = mapped_column(String(80), nullable=True, index=True)
    source_path: Mapped[str | None] = mapped_column(Text, nullable=True)
    api_version: Mapped[str | None] = mapped_column(String(100), nullable=True)
    schema_version: Mapped[str | None] = mapped_column(String(100), nullable=True, index=True)
    export_kind: Mapped[str | None] = mapped_column(String(100), nullable=True, index=True)
    exported_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True, index=True)
    netkingdom_id: Mapped[str | None] = mapped_column(Text, nullable=True)
    king_actor_id: Mapped[str | None] = mapped_column(Text, nullable=True)
    actor_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0, server_default="0")
    fabric_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0, server_default="0")
    unresolved_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0, server_default="0")
    compatibility: Mapped[dict] = mapped_column(JSONB, nullable=False, default=dict, server_default="{}")
    content_hash: Mapped[str] = mapped_column(String(64), nullable=False, index=True)
    node_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0, server_default="0")
    edge_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0, server_default="0")
    validation_status: Mapped[str] = mapped_column(String(20), nullable=False, default="valid", server_default="valid", index=True)
    error_details: Mapped[dict | None] = mapped_column(JSONB, nullable=True)
    graph_json: Mapped[dict] = mapped_column(JSONB, nullable=False, default=dict, server_default="{}")
    is_latest: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, server_default="false", index=True)
    last_seen_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)

    nodes: Mapped[list["FabricGraphNode"]] = relationship(
        "FabricGraphNode", back_populates="import_run", cascade="all, delete-orphan", lazy="select"
    )
    edges: Mapped[list["FabricGraphEdge"]] = relationship(
        "FabricGraphEdge", back_populates="import_run", cascade="all, delete-orphan", lazy="select"
    )


class FabricGraphNode(Base):
    __tablename__ = "fabric_graph_nodes"
    __table_args__ = (
        UniqueConstraint("import_id", "graph_id", name="uq_fabric_graph_nodes_import_graph_id"),
    )

    id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True), primary_key=True, default=new_uuid
    )
    import_id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True),
        ForeignKey("fabric_graph_imports.id", ondelete="CASCADE"),
        nullable=False,
        index=True,
    )
    source_repo_slug: Mapped[str] = mapped_column(String(100), nullable=False, index=True)
    graph_id: Mapped[str] = mapped_column(Text, nullable=False)
    kind: Mapped[str] = mapped_column(String(100), nullable=False, index=True)
    name: Mapped[str] = mapped_column(Text, nullable=False)
    repo_slug: Mapped[str] = mapped_column(String(100), nullable=False, index=True)
    domain_slug: Mapped[str] = mapped_column(String(100), nullable=False, index=True)
    lifecycle: Mapped[str] = mapped_column(String(50), nullable=False, index=True)
    canonical_type: Mapped[str | None] = mapped_column(String(100), nullable=True, index=True)
    canon_category: Mapped[str | None] = mapped_column(String(100), nullable=True, index=True)
    canon_anchor: Mapped[str | None] = mapped_column(Text, nullable=True)
    mapping_fit: Mapped[str | None] = mapped_column(String(20), nullable=True, index=True)
    evidence_state: Mapped[str | None] = mapped_column(String(20), nullable=True, index=True)
    evidence_review_state: Mapped[str | None] = mapped_column(String(20), nullable=True, index=True)
    evidence_confidence: Mapped[float | None] = mapped_column(Float, nullable=True)
    netkingdom_id: Mapped[str | None] = mapped_column(Text, nullable=True)
    fabric_id: Mapped[str | None] = mapped_column(Text, nullable=True, index=True)
    subfabric_id: Mapped[str | None] = mapped_column(Text, nullable=True, index=True)
    environment: Mapped[str | None] = mapped_column(String(100), nullable=True, index=True)
    deployment_scenario_id: Mapped[str | None] = mapped_column(Text, nullable=True)
    owner_actor_id: Mapped[str | None] = mapped_column(Text, nullable=True, index=True)
    owner_role: Mapped[str | None] = mapped_column(String(20), nullable=True, index=True)
    ownership_resolution: Mapped[str | None] = mapped_column(String(20), nullable=True, index=True)
    cost_center_id: Mapped[str | None] = mapped_column(Text, nullable=True, index=True)
    profit_center_id: Mapped[str | None] = mapped_column(Text, nullable=True, index=True)
    display_only: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, server_default="false", index=True)
    attributes: Mapped[dict] = mapped_column(JSONB, nullable=False, default=dict, server_default="{}")
    raw_json: Mapped[dict] = mapped_column(JSONB, nullable=False, default=dict, server_default="{}")

    import_run: Mapped["FabricGraphImport"] = relationship("FabricGraphImport", back_populates="nodes")


class FabricGraphEdge(Base):
    __tablename__ = "fabric_graph_edges"
    __table_args__ = (
        UniqueConstraint("import_id", "edge_key", name="uq_fabric_graph_edges_import_edge_key"),
    )

    id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True), primary_key=True, default=new_uuid
    )
    import_id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True),
        ForeignKey("fabric_graph_imports.id", ondelete="CASCADE"),
        nullable=False,
        index=True,
    )
    source_repo_slug: Mapped[str] = mapped_column(String(100), nullable=False, index=True)
    edge_key: Mapped[str] = mapped_column(String(64), nullable=False, index=True)
    from_graph_id: Mapped[str] = mapped_column(Text, nullable=False)
    to_graph_id: Mapped[str] = mapped_column(Text, nullable=False)
    edge_type: Mapped[str] = mapped_column(String(100), nullable=False, index=True)
    canonical_type: Mapped[str | None] = mapped_column(String(100), nullable=True, index=True)
    canon_anchor: Mapped[str | None] = mapped_column(Text, nullable=True)
    mapping_fit: Mapped[str | None] = mapped_column(String(20), nullable=True, index=True)
    evidence_state: Mapped[str | None] = mapped_column(String(20), nullable=True, index=True)
    evidence_review_state: Mapped[str | None] = mapped_column(String(20), nullable=True, index=True)
    evidence_confidence: Mapped[float | None] = mapped_column(Float, nullable=True)
    relationship_category: Mapped[str | None] = mapped_column(String(30), nullable=True, index=True)
    provider_owner_actor_id: Mapped[str | None] = mapped_column(Text, nullable=True, index=True)
    provider_fabric_id: Mapped[str | None] = mapped_column(Text, nullable=True, index=True)
    provider_subfabric_id: Mapped[str | None] = mapped_column(Text, nullable=True, index=True)
    consumer_owner_actor_id: Mapped[str | None] = mapped_column(Text, nullable=True, index=True)
    consumer_fabric_id: Mapped[str | None] = mapped_column(Text, nullable=True, index=True)
    consumer_subfabric_id: Mapped[str | None] = mapped_column(Text, nullable=True, index=True)
    crosses_fabric_boundary: Mapped[bool | None] = mapped_column(Boolean, nullable=True, index=True)
    crosses_subfabric_boundary: Mapped[bool | None] = mapped_column(Boolean, nullable=True, index=True)
    utility_type: Mapped[str | None] = mapped_column(String(100), nullable=True, index=True)
    utility_contract_id: Mapped[str | None] = mapped_column(Text, nullable=True)
    utility_payment_schema_id: Mapped[str | None] = mapped_column(Text, nullable=True, index=True)
    utility_metering_basis: Mapped[str | None] = mapped_column(String(100), nullable=True)
    utility_business_model: Mapped[str | None] = mapped_column(String(100), nullable=True, index=True)
    cost_center_id: Mapped[str | None] = mapped_column(Text, nullable=True, index=True)
    profit_center_id: Mapped[str | None] = mapped_column(Text, nullable=True, index=True)
    provider_profit_center_id: Mapped[str | None] = mapped_column(Text, nullable=True, index=True)
    consumer_cost_center_id: Mapped[str | None] = mapped_column(Text, nullable=True, index=True)
    display_only: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, server_default="false", index=True)
    attributes: Mapped[dict] = mapped_column(JSONB, nullable=False, default=dict, server_default="{}")
    raw_json: Mapped[dict] = mapped_column(JSONB, nullable=False, default=dict, server_default="{}")

    import_run: Mapped["FabricGraphImport"] = relationship("FabricGraphImport", back_populates="edges")
