"""SQLAlchemy ORM models for the flight database."""

from __future__ import annotations

import uuid
from datetime import datetime, timezone

from sqlalchemy import (
    JSON,
    Boolean,
    DateTime,
    Float,
    ForeignKey,
    Index,
    Integer,
    String,
    Text,
)
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship


def _utcnow() -> datetime:
    return datetime.now(tz=timezone.utc)


def _new_id() -> str:
    return uuid.uuid4().hex


class Base(DeclarativeBase):
    """Declarative base for all ORM models."""


# ── Flights ───────────────────────────────────────────────────────────────


class FlightRow(Base):
    __tablename__ = "flights"

    id: Mapped[str] = mapped_column(String(64), primary_key=True, default=_new_id)
    name: Mapped[str] = mapped_column(String(255), nullable=False)
    description: Mapped[str] = mapped_column(Text, default="")
    start_lat: Mapped[float] = mapped_column(Float, nullable=False)
    start_lon: Mapped[float] = mapped_column(Float, nullable=False)
    altitude: Mapped[float] = mapped_column(Float, nullable=False)
    camera_params: Mapped[dict] = mapped_column(JSON, nullable=False)
    created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_utcnow)
    updated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), default=_utcnow, onupdate=_utcnow
    )

    # relationships (cascade delete)
    waypoints: Mapped[list[WaypointRow]] = relationship(
        back_populates="flight", cascade="all, delete-orphan", passive_deletes=True
    )
    geofences: Mapped[list[GeofenceRow]] = relationship(
        back_populates="flight", cascade="all, delete-orphan", passive_deletes=True
    )
    state: Mapped[FlightStateRow | None] = relationship(
        back_populates="flight", cascade="all, delete-orphan", passive_deletes=True, uselist=False
    )
    frame_results: Mapped[list[FrameResultRow]] = relationship(
        back_populates="flight", cascade="all, delete-orphan", passive_deletes=True
    )
    headings: Mapped[list[HeadingRow]] = relationship(
        back_populates="flight", cascade="all, delete-orphan", passive_deletes=True
    )
    images: Mapped[list[ImageRow]] = relationship(
        back_populates="flight", cascade="all, delete-orphan", passive_deletes=True
    )
    chunks: Mapped[list[ChunkRow]] = relationship(
        back_populates="flight", cascade="all, delete-orphan", passive_deletes=True
    )


# ── Waypoints ─────────────────────────────────────────────────────────────


class WaypointRow(Base):
    __tablename__ = "waypoints"

    id: Mapped[str] = mapped_column(String(64), primary_key=True, default=_new_id)
    flight_id: Mapped[str] = mapped_column(
        ForeignKey("flights.id", ondelete="CASCADE"), nullable=False, index=True
    )
    lat: Mapped[float] = mapped_column(Float, nullable=False)
    lon: Mapped[float] = mapped_column(Float, nullable=False)
    altitude: Mapped[float | None] = mapped_column(Float, nullable=True)
    confidence: Mapped[float] = mapped_column(Float, default=0.0)
    timestamp: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_utcnow)
    refined: Mapped[bool] = mapped_column(Boolean, default=False)

    flight: Mapped[FlightRow] = relationship(back_populates="waypoints")

    __table_args__ = (Index("ix_waypoints_flight_ts", "flight_id", "timestamp"),)


# ── Geofences ─────────────────────────────────────────────────────────────


class GeofenceRow(Base):
    __tablename__ = "geofences"

    id: Mapped[str] = mapped_column(String(64), primary_key=True, default=_new_id)
    flight_id: Mapped[str] = mapped_column(
        ForeignKey("flights.id", ondelete="CASCADE"), nullable=False, index=True
    )
    nw_lat: Mapped[float] = mapped_column(Float, nullable=False)
    nw_lon: Mapped[float] = mapped_column(Float, nullable=False)
    se_lat: Mapped[float] = mapped_column(Float, nullable=False)
    se_lon: Mapped[float] = mapped_column(Float, nullable=False)

    flight: Mapped[FlightRow] = relationship(back_populates="geofences")


# ── Flight State ──────────────────────────────────────────────────────────


class FlightStateRow(Base):
    __tablename__ = "flight_state"

    flight_id: Mapped[str] = mapped_column(
        ForeignKey("flights.id", ondelete="CASCADE"), primary_key=True
    )
    status: Mapped[str] = mapped_column(String(32), default="created")
    frames_processed: Mapped[int] = mapped_column(Integer, default=0)
    frames_total: Mapped[int] = mapped_column(Integer, default=0)
    current_frame: Mapped[int | None] = mapped_column(Integer, nullable=True)
    blocked: Mapped[bool] = mapped_column(Boolean, default=False)
    search_grid_size: Mapped[int | None] = mapped_column(Integer, nullable=True)
    created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_utcnow)
    updated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), default=_utcnow, onupdate=_utcnow
    )

    flight: Mapped[FlightRow] = relationship(back_populates="state")


# ── Frame Results ─────────────────────────────────────────────────────────


class FrameResultRow(Base):
    __tablename__ = "frame_results"

    id: Mapped[str] = mapped_column(String(64), primary_key=True, default=_new_id)
    flight_id: Mapped[str] = mapped_column(
        ForeignKey("flights.id", ondelete="CASCADE"), nullable=False, index=True
    )
    frame_id: Mapped[int] = mapped_column(Integer, nullable=False)
    gps_lat: Mapped[float] = mapped_column(Float, nullable=False)
    gps_lon: Mapped[float] = mapped_column(Float, nullable=False)
    altitude: Mapped[float] = mapped_column(Float, default=0.0)
    heading: Mapped[float] = mapped_column(Float, default=0.0)
    confidence: Mapped[float] = mapped_column(Float, default=0.0)
    refined: Mapped[bool] = mapped_column(Boolean, default=False)
    timestamp: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_utcnow)

    flight: Mapped[FlightRow] = relationship(back_populates="frame_results")

    __table_args__ = (
        Index("ix_frame_results_flight_frame", "flight_id", "frame_id", unique=True),
    )


# ── Heading History ───────────────────────────────────────────────────────


class HeadingRow(Base):
    __tablename__ = "heading_history"

    id: Mapped[str] = mapped_column(String(64), primary_key=True, default=_new_id)
    flight_id: Mapped[str] = mapped_column(
        ForeignKey("flights.id", ondelete="CASCADE"), nullable=False, index=True
    )
    frame_id: Mapped[int] = mapped_column(Integer, nullable=False)
    heading: Mapped[float] = mapped_column(Float, nullable=False)
    timestamp: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_utcnow)

    flight: Mapped[FlightRow] = relationship(back_populates="headings")

    __table_args__ = (
        Index("ix_heading_flight_frame", "flight_id", "frame_id", unique=True),
    )


# ── Images ────────────────────────────────────────────────────────────────


class ImageRow(Base):
    __tablename__ = "images"

    id: Mapped[str] = mapped_column(String(64), primary_key=True, default=_new_id)
    flight_id: Mapped[str] = mapped_column(
        ForeignKey("flights.id", ondelete="CASCADE"), nullable=False, index=True
    )
    frame_id: Mapped[int] = mapped_column(Integer, nullable=False)
    file_path: Mapped[str] = mapped_column(Text, nullable=False)
    metadata_json: Mapped[dict | None] = mapped_column(JSON, nullable=True)

    flight: Mapped[FlightRow] = relationship(back_populates="images")

    __table_args__ = (
        Index("ix_images_flight_frame", "flight_id", "frame_id", unique=True),
    )


# ── Chunks ────────────────────────────────────────────────────────────────


class ChunkRow(Base):
    __tablename__ = "chunks"

    chunk_id: Mapped[str] = mapped_column(String(64), primary_key=True, default=_new_id)
    flight_id: Mapped[str] = mapped_column(
        ForeignKey("flights.id", ondelete="CASCADE"), nullable=False, index=True
    )
    start_frame_id: Mapped[int] = mapped_column(Integer, nullable=False)
    end_frame_id: Mapped[int | None] = mapped_column(Integer, nullable=True)
    frames: Mapped[list] = mapped_column(JSON, default=list)
    is_active: Mapped[bool] = mapped_column(Boolean, default=True)
    has_anchor: Mapped[bool] = mapped_column(Boolean, default=False)
    anchor_frame_id: Mapped[int | None] = mapped_column(Integer, nullable=True)
    anchor_lat: Mapped[float | None] = mapped_column(Float, nullable=True)
    anchor_lon: Mapped[float | None] = mapped_column(Float, nullable=True)
    matching_status: Mapped[str] = mapped_column(String(32), default="pending")
    updated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), default=_utcnow, onupdate=_utcnow
    )

    flight: Mapped[FlightRow] = relationship(back_populates="chunks")
