"""SQLAlchemy 2.0 declarative models for ACE-F."""
from __future__ import annotations

import datetime as dt
import uuid

from sqlalchemy import (
    BigInteger,
    Boolean,
    Date,
    DateTime,
    ForeignKey,
    Index,
    Integer,
    Numeric,
    Text,
    UniqueConstraint,
)
from sqlalchemy.dialects.postgresql import JSONB, UUID
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship

# ---------------------------------------------------------------------------
# Phase 3 imports (used below in new model definitions)
# ---------------------------------------------------------------------------


def _utcnow() -> dt.datetime:
    return dt.datetime.now(tz=dt.UTC)


class Base(DeclarativeBase):
    pass


class IssuerMaster(Base):
    __tablename__ = "issuer_master"

    issuer_id: Mapped[str] = mapped_column(Text, primary_key=True)
    cik: Mapped[str | None] = mapped_column(Text, unique=True, nullable=True)
    ticker: Mapped[str | None] = mapped_column(Text, nullable=True)
    issuer_name: Mapped[str] = mapped_column(Text, nullable=False)
    exchange: Mapped[str | None] = mapped_column(Text, nullable=True)
    country_code: Mapped[str | None] = mapped_column(Text, nullable=True)
    is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
    created_at_utc: Mapped[dt.datetime] = mapped_column(
        DateTime(timezone=True), default=_utcnow, nullable=False
    )
    updated_at_utc: Mapped[dt.datetime] = mapped_column(
        DateTime(timezone=True), default=_utcnow, onupdate=_utcnow, nullable=False
    )

    symbols: Mapped[list[SymbolMaster]] = relationship(back_populates="issuer")


class SymbolMaster(Base):
    __tablename__ = "symbol_master"

    symbol_id: Mapped[str] = mapped_column(Text, primary_key=True)
    issuer_id: Mapped[str | None] = mapped_column(
        Text, ForeignKey("issuer_master.issuer_id"), nullable=True
    )
    ticker: Mapped[str] = mapped_column(Text, nullable=False)
    venue: Mapped[str | None] = mapped_column(Text, nullable=True)
    asset_type: Mapped[str | None] = mapped_column(Text, nullable=True)
    currency: Mapped[str | None] = mapped_column(Text, nullable=True)
    start_date: Mapped[dt.date | None] = mapped_column(Date, nullable=True)
    end_date: Mapped[dt.date | None] = mapped_column(Date, nullable=True)
    is_primary: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
    created_at_utc: Mapped[dt.datetime] = mapped_column(
        DateTime(timezone=True), default=_utcnow, nullable=False
    )
    updated_at_utc: Mapped[dt.datetime] = mapped_column(
        DateTime(timezone=True), default=_utcnow, onupdate=_utcnow, nullable=False
    )

    issuer: Mapped[IssuerMaster | None] = relationship(back_populates="symbols")


class JobRun(Base):
    __tablename__ = "job_runs"
    __table_args__ = (Index("ix_job_runs_job_name_run_date", "job_name", "run_date"),)

    job_run_id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
    )
    job_name: Mapped[str] = mapped_column(Text, nullable=False)
    source_name: Mapped[str | None] = mapped_column(Text, nullable=True)
    run_date: Mapped[dt.date | None] = mapped_column(Date, nullable=True)
    status: Mapped[str] = mapped_column(Text, nullable=False)
    started_at_utc: Mapped[dt.datetime] = mapped_column(
        DateTime(timezone=True), default=_utcnow, nullable=False
    )
    finished_at_utc: Mapped[dt.datetime | None] = mapped_column(
        DateTime(timezone=True), nullable=True
    )
    records_seen: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    records_written: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    records_skipped: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    error_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    error_summary: Mapped[str | None] = mapped_column(Text, nullable=True)
    metadata_json: Mapped[dict] = mapped_column(JSONB, default=dict, nullable=False)


class Document(Base):
    __tablename__ = "documents"
    __table_args__ = (
        UniqueConstraint("accession_no", "form_type", name="uq_documents_accession_form"),
        Index("ix_documents_issuer_filing_date", "issuer_id", "filing_date"),
        Index("ix_documents_form_type_filing_date", "form_type", "filing_date"),
    )

    document_id: Mapped[str] = mapped_column(Text, primary_key=True)
    source_name: Mapped[str] = mapped_column(Text, nullable=False)
    issuer_id: Mapped[str | None] = mapped_column(
        Text, ForeignKey("issuer_master.issuer_id"), nullable=True
    )
    symbol_id: Mapped[str | None] = mapped_column(
        Text, ForeignKey("symbol_master.symbol_id"), nullable=True
    )
    accession_no: Mapped[str | None] = mapped_column(Text, nullable=True)
    form_type: Mapped[str] = mapped_column(Text, nullable=False)
    filing_date: Mapped[dt.date] = mapped_column(Date, nullable=False)
    accepted_at_utc: Mapped[dt.datetime | None] = mapped_column(
        DateTime(timezone=True), nullable=True
    )
    primary_document_name: Mapped[str | None] = mapped_column(Text, nullable=True)
    item_numbers: Mapped[list | None] = mapped_column(JSONB, nullable=True)
    parsed_status: Mapped[str] = mapped_column(Text, default="pending", nullable=False)
    created_at_utc: Mapped[dt.datetime] = mapped_column(
        DateTime(timezone=True), default=_utcnow, nullable=False
    )
    updated_at_utc: Mapped[dt.datetime] = mapped_column(
        DateTime(timezone=True), default=_utcnow, onupdate=_utcnow, nullable=False
    )

    exhibits: Mapped[list[DocumentExhibit]] = relationship(back_populates="document")
    events: Mapped[list[Event]] = relationship(back_populates="primary_document")


class DocumentExhibit(Base):
    __tablename__ = "document_exhibits"

    exhibit_id: Mapped[str] = mapped_column(Text, primary_key=True)
    document_id: Mapped[str] = mapped_column(
        Text, ForeignKey("documents.document_id"), nullable=False
    )
    exhibit_code: Mapped[str] = mapped_column(Text, nullable=False)
    exhibit_name: Mapped[str | None] = mapped_column(Text, nullable=True)
    created_at_utc: Mapped[dt.datetime] = mapped_column(
        DateTime(timezone=True), default=_utcnow, nullable=False
    )

    document: Mapped[Document] = relationship(back_populates="exhibits")


class ExhibitCache(Base):
    __tablename__ = "exhibit_cache"
    __table_args__ = (
        UniqueConstraint("accession_no", "exhibit_type", name="uq_exhibit_cache_accession_type"),
    )

    id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
    accession_no: Mapped[str] = mapped_column(Text, nullable=False)
    exhibit_type: Mapped[str] = mapped_column(Text, nullable=False)
    content_hash: Mapped[str] = mapped_column(Text, nullable=False)
    cache_path: Mapped[str] = mapped_column(Text, nullable=False)
    fetched_at_utc: Mapped[dt.datetime] = mapped_column(
        DateTime(timezone=True), default=_utcnow, nullable=False
    )


class Event(Base):
    __tablename__ = "events"
    __table_args__ = (
        Index("ix_events_event_type_date", "event_type", "event_date"),
        Index("ix_events_primary_document_id", "primary_document_id"),
    )

    event_id: Mapped[str] = mapped_column(Text, primary_key=True)
    issuer_id: Mapped[str | None] = mapped_column(
        Text, ForeignKey("issuer_master.issuer_id"), nullable=True
    )
    symbol_id: Mapped[str | None] = mapped_column(
        Text, ForeignKey("symbol_master.symbol_id"), nullable=True
    )
    primary_document_id: Mapped[str] = mapped_column(
        Text, ForeignKey("documents.document_id"), nullable=False
    )
    event_type: Mapped[str] = mapped_column(Text, nullable=False)
    event_direction: Mapped[str] = mapped_column(Text, nullable=False)
    event_date: Mapped[dt.date] = mapped_column(Date, nullable=False)
    filed_at_utc: Mapped[dt.datetime | None] = mapped_column(
        DateTime(timezone=True), nullable=True
    )
    parser_version: Mapped[str] = mapped_column(Text, nullable=False)
    parse_confidence: Mapped[float | None] = mapped_column(Numeric, nullable=True)
    status: Mapped[str] = mapped_column(Text, default="pending", nullable=False)
    created_at_utc: Mapped[dt.datetime] = mapped_column(
        DateTime(timezone=True), default=_utcnow, nullable=False
    )
    updated_at_utc: Mapped[dt.datetime] = mapped_column(
        DateTime(timezone=True), default=_utcnow, onupdate=_utcnow, nullable=False
    )

    primary_document: Mapped[Document] = relationship(back_populates="events")
    parses: Mapped[list[EventParse]] = relationship(back_populates="event")
    feature_snapshots: Mapped[list[FeatureSnapshot]] = relationship(back_populates="event")


class EventParse(Base):
    __tablename__ = "event_parses"

    event_parse_id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
    event_id: Mapped[str] = mapped_column(
        Text, ForeignKey("events.event_id"), nullable=False
    )
    parser_kind: Mapped[str] = mapped_column(Text, nullable=False)
    parser_version: Mapped[str] = mapped_column(Text, nullable=False)
    schema_version: Mapped[str] = mapped_column(Text, nullable=False)
    output_json: Mapped[dict] = mapped_column(JSONB, nullable=False)
    validation_status: Mapped[str] = mapped_column(Text, nullable=False)
    validation_errors: Mapped[dict | None] = mapped_column(JSONB, nullable=True)
    created_at_utc: Mapped[dt.datetime] = mapped_column(
        DateTime(timezone=True), default=_utcnow, nullable=False
    )

    event: Mapped[Event] = relationship(back_populates="parses")


class MacroSeries(Base):
    __tablename__ = "macro_series"

    series_id: Mapped[str] = mapped_column(Text, primary_key=True)
    title: Mapped[str | None] = mapped_column(Text, nullable=True)
    frequency: Mapped[str | None] = mapped_column(Text, nullable=True)
    units: Mapped[str | None] = mapped_column(Text, nullable=True)
    source_name: Mapped[str] = mapped_column(Text, nullable=False)
    metadata_json: Mapped[dict] = mapped_column(JSONB, default=dict, nullable=False)
    created_at_utc: Mapped[dt.datetime] = mapped_column(
        DateTime(timezone=True), default=_utcnow, nullable=False
    )

    observations: Mapped[list[MacroObservation]] = relationship(back_populates="series")


class MacroObservation(Base):
    __tablename__ = "macro_observations"
    __table_args__ = (
        UniqueConstraint("series_id", "observation_date", name="uq_macro_obs_series_date"),
    )

    id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
    series_id: Mapped[str] = mapped_column(
        Text, ForeignKey("macro_series.series_id"), nullable=False
    )
    observation_date: Mapped[dt.date] = mapped_column(Date, nullable=False)
    value: Mapped[float | None] = mapped_column(Numeric, nullable=True)
    created_at_utc: Mapped[dt.datetime] = mapped_column(
        DateTime(timezone=True), default=_utcnow, nullable=False
    )

    series: Mapped[MacroSeries] = relationship(back_populates="observations")


class ShortSaleDaily(Base):
    __tablename__ = "short_sale_daily"
    __table_args__ = (
        UniqueConstraint(
            "ticker_raw", "trade_date", "source_name", name="uq_short_sale_ticker_date_source"
        ),
        Index("ix_short_sale_ticker_date", "ticker_raw", "trade_date"),
    )

    id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
    symbol_id: Mapped[str | None] = mapped_column(
        Text, ForeignKey("symbol_master.symbol_id"), nullable=True
    )
    ticker_raw: Mapped[str] = mapped_column(Text, nullable=False)
    trade_date: Mapped[dt.date] = mapped_column(Date, nullable=False)
    short_volume: Mapped[int] = mapped_column(BigInteger, nullable=False)
    short_exempt_volume: Mapped[int | None] = mapped_column(BigInteger, nullable=True)
    total_volume: Mapped[int | None] = mapped_column(BigInteger, nullable=True)
    source_name: Mapped[str] = mapped_column(Text, nullable=False)
    created_at_utc: Mapped[dt.datetime] = mapped_column(
        DateTime(timezone=True), default=_utcnow, nullable=False
    )


class FeatureSnapshot(Base):
    __tablename__ = "feature_snapshots"

    feature_snapshot_id: Mapped[int] = mapped_column(
        BigInteger, primary_key=True, autoincrement=True
    )
    event_id: Mapped[str] = mapped_column(
        Text, ForeignKey("events.event_id"), nullable=False
    )
    snapshot_name: Mapped[str] = mapped_column(Text, nullable=False)
    snapshot_version: Mapped[str] = mapped_column(Text, nullable=False)
    feature_json: Mapped[dict] = mapped_column(JSONB, nullable=False)
    created_at_utc: Mapped[dt.datetime] = mapped_column(
        DateTime(timezone=True), default=_utcnow, nullable=False
    )

    event: Mapped[Event] = relationship(back_populates="feature_snapshots")


class OrderPlan(Base):
    __tablename__ = "order_plans"

    order_plan_id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
    event_id: Mapped[str] = mapped_column(
        Text, ForeignKey("events.event_id"), nullable=False
    )
    symbol_id: Mapped[str] = mapped_column(
        Text, ForeignKey("symbol_master.symbol_id"), nullable=False
    )
    side: Mapped[str] = mapped_column(Text, nullable=False)
    planned_entry_date: Mapped[dt.date] = mapped_column(Date, nullable=False)
    planned_order_type: Mapped[str] = mapped_column(Text, nullable=False)
    planned_price: Mapped[float | None] = mapped_column(Numeric, nullable=True)
    stop_price: Mapped[float | None] = mapped_column(Numeric, nullable=True)
    take_profit_price: Mapped[float | None] = mapped_column(Numeric, nullable=True)
    quantity_plan: Mapped[float | None] = mapped_column(Numeric, nullable=True)
    status: Mapped[str] = mapped_column(Text, default="draft", nullable=False)
    created_at_utc: Mapped[dt.datetime] = mapped_column(
        DateTime(timezone=True), default=_utcnow, nullable=False
    )


class SyncCheckpoint(Base):
    __tablename__ = "sync_checkpoints"
    __table_args__ = (UniqueConstraint("domain", name="uq_sync_checkpoints_domain"),)

    id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
    domain: Mapped[str] = mapped_column(Text, nullable=False)
    last_sync_at_utc: Mapped[dt.datetime | None] = mapped_column(
        DateTime(timezone=True), nullable=True
    )
    last_sync_params: Mapped[dict] = mapped_column(JSONB, default=dict, nullable=False)
    status: Mapped[str] = mapped_column(Text, nullable=False)
    created_at_utc: Mapped[dt.datetime] = mapped_column(
        DateTime(timezone=True), default=_utcnow, nullable=False
    )
    updated_at_utc: Mapped[dt.datetime] = mapped_column(
        DateTime(timezone=True), default=_utcnow, onupdate=_utcnow, nullable=False
    )


# ---------------------------------------------------------------------------
# Phase 3 models
# ---------------------------------------------------------------------------


class LLMCallCache(Base):
    """Cache of LLM API calls keyed by content hash."""

    __tablename__ = "llm_call_cache"

    cache_key: Mapped[str] = mapped_column(Text, primary_key=True)
    document_id: Mapped[str | None] = mapped_column(Text, nullable=True)
    model_name: Mapped[str] = mapped_column(Text, nullable=False)
    prompt_version: Mapped[str] = mapped_column(Text, nullable=False)
    schema_version: Mapped[str] = mapped_column(Text, nullable=False)
    raw_prompt: Mapped[str] = mapped_column(Text, nullable=False)
    raw_response: Mapped[str] = mapped_column(Text, nullable=False)
    normalized_json: Mapped[dict] = mapped_column(JSONB, nullable=False)
    token_usage_json: Mapped[dict] = mapped_column(JSONB, nullable=False)
    elapsed_ms: Mapped[int] = mapped_column(Integer, nullable=False)
    created_at_utc: Mapped[dt.datetime] = mapped_column(
        DateTime(timezone=True), default=_utcnow, nullable=False
    )


class ReviewItem(Base):
    """Human-in-the-loop review queue item."""

    __tablename__ = "review_items"
    __table_args__ = (
        Index("ix_review_items_entity_open", "entity_type", "entity_id", "status"),
        Index("ix_review_items_status_priority", "status", "priority"),
    )

    review_id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
    )
    entity_type: Mapped[str] = mapped_column(Text, nullable=False)
    entity_id: Mapped[str] = mapped_column(Text, nullable=False)
    priority: Mapped[str] = mapped_column(Text, nullable=False)
    reason_codes: Mapped[list] = mapped_column(JSONB, nullable=False)
    status: Mapped[str] = mapped_column(Text, default="open", nullable=False)
    assigned_to: Mapped[str | None] = mapped_column(Text, nullable=True)
    snapshot_refs: Mapped[dict] = mapped_column(JSONB, nullable=False)
    suggested_overrides: Mapped[dict | None] = mapped_column(JSONB, nullable=True)
    reviewer_id: Mapped[str | None] = mapped_column(Text, nullable=True)
    resolution_type: Mapped[str | None] = mapped_column(Text, nullable=True)
    field_overrides: Mapped[dict | None] = mapped_column(JSONB, nullable=True)
    root_cause: Mapped[str | None] = mapped_column(Text, nullable=True)
    notes: Mapped[str | None] = mapped_column(Text, nullable=True)
    resolved_at: Mapped[dt.datetime | None] = mapped_column(
        DateTime(timezone=True), nullable=True
    )
    created_at_utc: Mapped[dt.datetime] = mapped_column(
        DateTime(timezone=True), default=_utcnow, nullable=False
    )
    updated_at_utc: Mapped[dt.datetime] = mapped_column(
        DateTime(timezone=True), default=_utcnow, onupdate=_utcnow, nullable=False
    )


class EventLabel(Base):
    """Forward-return labels for a parsed event."""

    __tablename__ = "event_labels"
    __table_args__ = (
        UniqueConstraint(
            "event_id", "entry_convention", "label_version",
            name="uq_event_labels_event_convention_version",
        ),
    )

    label_id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
    event_id: Mapped[str] = mapped_column(
        Text, ForeignKey("events.event_id"), nullable=False
    )
    entry_convention: Mapped[str] = mapped_column(Text, nullable=False)
    reaction_date: Mapped[dt.date | None] = mapped_column(Date, nullable=True)
    entry_date: Mapped[dt.date | None] = mapped_column(Date, nullable=True)
    entry_price: Mapped[float | None] = mapped_column(Numeric, nullable=True)
    fwd_return_1d: Mapped[float | None] = mapped_column(Numeric, nullable=True)
    fwd_return_3d: Mapped[float | None] = mapped_column(Numeric, nullable=True)
    fwd_return_5d: Mapped[float | None] = mapped_column(Numeric, nullable=True)
    fwd_return_10d: Mapped[float | None] = mapped_column(Numeric, nullable=True)
    fwd_return_20d: Mapped[float | None] = mapped_column(Numeric, nullable=True)
    hit_pos_1r_within_3d: Mapped[bool | None] = mapped_column(Boolean, nullable=True)
    hit_neg_1r_within_3d: Mapped[bool | None] = mapped_column(Boolean, nullable=True)
    close_up_after_3d: Mapped[bool | None] = mapped_column(Boolean, nullable=True)
    close_up_after_5d: Mapped[bool | None] = mapped_column(Boolean, nullable=True)
    mfe_3d: Mapped[float | None] = mapped_column(Numeric, nullable=True)
    mae_3d: Mapped[float | None] = mapped_column(Numeric, nullable=True)
    mfe_5d: Mapped[float | None] = mapped_column(Numeric, nullable=True)
    mae_5d: Mapped[float | None] = mapped_column(Numeric, nullable=True)
    mfe_10d: Mapped[float | None] = mapped_column(Numeric, nullable=True)
    mae_10d: Mapped[float | None] = mapped_column(Numeric, nullable=True)
    mfe_20d: Mapped[float | None] = mapped_column(Numeric, nullable=True)
    mae_20d: Mapped[float | None] = mapped_column(Numeric, nullable=True)
    bars_to_mfe_3d: Mapped[int | None] = mapped_column(Integer, nullable=True)
    bars_to_mae_3d: Mapped[int | None] = mapped_column(Integer, nullable=True)
    days_to_peak_close_5d: Mapped[int | None] = mapped_column(Integer, nullable=True)
    label_status: Mapped[str] = mapped_column(Text, nullable=False)
    invalid_event_for_labeling: Mapped[bool] = mapped_column(
        Boolean, default=False, nullable=False
    )
    risk_model_name: Mapped[str | None] = mapped_column(Text, nullable=True)
    initial_stop_price: Mapped[float | None] = mapped_column(Numeric, nullable=True)
    initial_r_value: Mapped[float | None] = mapped_column(Numeric, nullable=True)
    label_version: Mapped[str] = mapped_column(Text, nullable=False)
    created_at_utc: Mapped[dt.datetime] = mapped_column(
        DateTime(timezone=True), default=_utcnow, nullable=False
    )

    event: Mapped[Event] = relationship()
