from __future__ import annotations

from datetime import datetime, timezone

from sqlalchemy import DateTime, Integer, String, Text, UniqueConstraint
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column


def utc_now() -> datetime:
    return datetime.now(timezone.utc)


class Base(DeclarativeBase):
    pass


class RefreshRunRow(Base):
    __tablename__ = "refresh_runs"

    id: Mapped[str] = mapped_column(String(64), primary_key=True)
    mode: Mapped[str] = mapped_column(String(32), nullable=False)
    status: Mapped[str] = mapped_column(String(32), nullable=False, default="running")
    started_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, default=utc_now)
    finished_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    requested_entity_sets_json: Mapped[str] = mapped_column(Text, nullable=False, default="[]")
    date_from: Mapped[str | None] = mapped_column(String(64), nullable=True)
    date_to: Mapped[str | None] = mapped_column(String(64), nullable=True)
    limit_per_set: Mapped[int] = mapped_column(Integer, nullable=False, default=200)
    records_read: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    entities_written: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    links_written: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    checkpoints_updated: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    details_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
    error_message: Mapped[str | None] = mapped_column(Text, nullable=True)


class CanonicalEntityRow(Base):
    __tablename__ = "canonical_entities"
    __table_args__ = (
        UniqueConstraint("source_entity", "source_id", name="uq_canonical_entities_source"),
    )

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    source_entity: Mapped[str] = mapped_column(String(255), nullable=False)
    source_id: Mapped[str] = mapped_column(String(255), nullable=False)
    display_name: Mapped[str] = mapped_column(Text, nullable=False, default="")
    attributes_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
    first_seen_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, default=utc_now)
    updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, default=utc_now)
    last_refresh_run_id: Mapped[str | None] = mapped_column(String(64), nullable=True)


class CanonicalLinkRow(Base):
    __tablename__ = "canonical_links"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    source_entity: Mapped[str] = mapped_column(String(255), nullable=False)
    source_id: Mapped[str] = mapped_column(String(255), nullable=False)
    relation: Mapped[str] = mapped_column(String(128), nullable=False)
    target_entity: Mapped[str] = mapped_column(String(255), nullable=False)
    target_id: Mapped[str] = mapped_column(String(255), nullable=False)
    source_field: Mapped[str | None] = mapped_column(String(255), nullable=True)
    updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, default=utc_now)
    last_refresh_run_id: Mapped[str | None] = mapped_column(String(64), nullable=True)


class RefreshCheckpointRow(Base):
    __tablename__ = "refresh_checkpoints"

    entity_set: Mapped[str] = mapped_column(String(255), primary_key=True)
    last_success_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, default=utc_now)
    last_refresh_run_id: Mapped[str] = mapped_column(String(64), nullable=False)
    last_date_from: Mapped[str | None] = mapped_column(String(64), nullable=True)
    last_date_to: Mapped[str | None] = mapped_column(String(64), nullable=True)


class FeatureRunRow(Base):
    __tablename__ = "feature_runs"

    id: Mapped[str] = mapped_column(String(64), primary_key=True)
    status: Mapped[str] = mapped_column(String(32), nullable=False, default="running")
    started_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, default=utc_now)
    finished_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    baseline_window_hours: Mapped[int] = mapped_column(Integer, nullable=False, default=24)
    entities_total: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    metrics_written: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    anomalies_written: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    details_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
    error_message: Mapped[str | None] = mapped_column(Text, nullable=True)


class FeatureMetricRow(Base):
    __tablename__ = "feature_metrics"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    feature_run_id: Mapped[str] = mapped_column(String(64), nullable=False)
    metric_key: Mapped[str] = mapped_column(String(128), nullable=False)
    scope: Mapped[str] = mapped_column(String(128), nullable=False)
    scope_id: Mapped[str] = mapped_column(String(255), nullable=False, default="")
    metric_type: Mapped[str] = mapped_column(String(32), nullable=False, default="gauge")
    metric_value: Mapped[float] = mapped_column(nullable=False, default=0.0)
    attributes_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
    computed_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, default=utc_now)


class AnomalySignalRow(Base):
    __tablename__ = "anomaly_signals"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    feature_run_id: Mapped[str] = mapped_column(String(64), nullable=False)
    signal_type: Mapped[str] = mapped_column(String(128), nullable=False)
    severity: Mapped[str] = mapped_column(String(32), nullable=False, default="medium")
    scope: Mapped[str] = mapped_column(String(128), nullable=False, default="global")
    scope_id: Mapped[str] = mapped_column(String(255), nullable=False, default="")
    score: Mapped[float] = mapped_column(nullable=False, default=0.0)
    details_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
    detected_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, default=utc_now)
    is_active: Mapped[int] = mapped_column(Integer, nullable=False, default=1)


class RiskRunRow(Base):
    __tablename__ = "risk_runs"

    id: Mapped[str] = mapped_column(String(64), primary_key=True)
    status: Mapped[str] = mapped_column(String(32), nullable=False, default="running")
    started_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, default=utc_now)
    finished_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    source_feature_run_id: Mapped[str | None] = mapped_column(String(64), nullable=True)
    patterns_written: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    global_score: Mapped[float] = mapped_column(nullable=False, default=0.0)
    details_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
    error_message: Mapped[str | None] = mapped_column(Text, nullable=True)


class RiskPatternRow(Base):
    __tablename__ = "risk_patterns"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    risk_run_id: Mapped[str] = mapped_column(String(64), nullable=False)
    pattern_key: Mapped[str] = mapped_column(String(128), nullable=False)
    severity: Mapped[str] = mapped_column(String(32), nullable=False, default="low")
    scope: Mapped[str] = mapped_column(String(128), nullable=False, default="global")
    scope_id: Mapped[str] = mapped_column(String(255), nullable=False, default="")
    score: Mapped[float] = mapped_column(nullable=False, default=0.0)
    confidence: Mapped[float] = mapped_column(nullable=False, default=0.0)
    details_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
    detected_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, default=utc_now)
    is_active: Mapped[int] = mapped_column(Integer, nullable=False, default=1)
