"""
Modèles SQLAlchemy pour la persistance des données.

Ce module contient tous les modèles de base de données avec leurs relations,
index et contraintes.
"""

from datetime import datetime
from typing import List

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

from pipeline_mco_pmsi.database.base import Base


class StayDB(Base):
    """Séjour hospitalier."""

    __tablename__ = "stays"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    stay_id: Mapped[str] = mapped_column(String(100), unique=True, nullable=False, index=True)
    admission_date: Mapped[datetime] = mapped_column(DateTime, nullable=False)
    discharge_date: Mapped[datetime] = mapped_column(DateTime, nullable=False)
    specialty: Mapped[str] = mapped_column(String(100), nullable=False, index=True)
    unit: Mapped[str | None] = mapped_column(String(100), nullable=True)
    age: Mapped[int | None] = mapped_column(Integer, nullable=True)
    sex: Mapped[str | None] = mapped_column(String(1), nullable=True)
    status: Mapped[str | None] = mapped_column(String(50), nullable=True, default="proposed")
    created_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow
    )
    updated_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow, onupdate=datetime.utcnow
    )

    # Relations
    documents: Mapped[List["ClinicalDocumentDB"]] = relationship(
        "ClinicalDocumentDB", back_populates="stay", cascade="all, delete-orphan"
    )
    facts: Mapped[List["ClinicalFactDB"]] = relationship(
        "ClinicalFactDB", back_populates="stay", cascade="all, delete-orphan"
    )
    codes: Mapped[List["CodeDB"]] = relationship(
        "CodeDB", back_populates="stay", cascade="all, delete-orphan"
    )
    validation_issues: Mapped[List["ValidationIssueDB"]] = relationship(
        "ValidationIssueDB", back_populates="stay", cascade="all, delete-orphan"
    )
    questions: Mapped[List["QuestionDB"]] = relationship(
        "QuestionDB", back_populates="stay", cascade="all, delete-orphan"
    )
    audit_records: Mapped[List["AuditRecordDB"]] = relationship(
        "AuditRecordDB", back_populates="stay", cascade="all, delete-orphan"
    )
    verification_results: Mapped[List["VerificationResultDB"]] = relationship(
        "VerificationResultDB", back_populates="stay", cascade="all, delete-orphan"
    )
    groupage_results: Mapped[List["GroupageResultDB"]] = relationship(
        "GroupageResultDB", back_populates="stay", cascade="all, delete-orphan"
    )

    __table_args__ = (
        Index("idx_stays_admission_date", "admission_date"),
        Index("idx_stays_specialty", "specialty"),
    )


class ClinicalDocumentDB(Base):
    """Document clinique."""

    __tablename__ = "clinical_documents"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    document_id: Mapped[str] = mapped_column(String(100), unique=True, nullable=False, index=True)
    stay_id: Mapped[int] = mapped_column(
        Integer, ForeignKey("stays.id", ondelete="CASCADE"), nullable=False, index=True
    )
    document_type: Mapped[str] = mapped_column(String(50), nullable=False, index=True)
    content: Mapped[str] = mapped_column(Text, nullable=False)
    creation_date: Mapped[datetime] = mapped_column(DateTime, nullable=False)
    author: Mapped[str | None] = mapped_column(String(200), nullable=True)
    priority: Mapped[int] = mapped_column(Integer, nullable=False)
    created_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow
    )

    # Relations
    stay: Mapped["StayDB"] = relationship("StayDB", back_populates="documents")
    evidences: Mapped[List["EvidenceDB"]] = relationship(
        "EvidenceDB", back_populates="document", cascade="all, delete-orphan"
    )

    __table_args__ = (
        Index("idx_documents_stay_id", "stay_id"),
        Index("idx_documents_type", "document_type"),
    )


class ClinicalFactDB(Base):
    """Fait clinique extrait."""

    __tablename__ = "clinical_facts"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    fact_id: Mapped[str] = mapped_column(String(100), unique=True, nullable=False, index=True)
    stay_id: Mapped[int] = mapped_column(
        Integer, ForeignKey("stays.id", ondelete="CASCADE"), nullable=False, index=True
    )
    type: Mapped[str] = mapped_column(String(50), nullable=False, index=True)
    text: Mapped[str] = mapped_column(Text, nullable=False)
    
    # Qualifier fields (dénormalisé pour performance)
    qualifier_certainty: Mapped[str] = mapped_column(String(20), nullable=False, index=True)
    qualifier_markers: Mapped[dict] = mapped_column(JSON, nullable=False, default=list)
    qualifier_confidence: Mapped[float] = mapped_column(Float, nullable=False)
    
    temporality: Mapped[str] = mapped_column(String(20), nullable=False, index=True)
    confidence: Mapped[float] = mapped_column(Float, nullable=False)
    
    # Evidence fields (dénormalisé)
    evidence_document_id: Mapped[str] = mapped_column(String(100), nullable=False)
    evidence_span_start: Mapped[int] = mapped_column(Integer, nullable=False)
    evidence_span_end: Mapped[int] = mapped_column(Integer, nullable=False)
    evidence_text: Mapped[str] = mapped_column(Text, nullable=False)
    evidence_context: Mapped[str | None] = mapped_column(Text, nullable=True)
    
    created_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow
    )

    # Relations
    stay: Mapped["StayDB"] = relationship("StayDB", back_populates="facts")

    __table_args__ = (
        Index("idx_facts_stay_id", "stay_id"),
        Index("idx_facts_type", "type"),
        Index("idx_facts_certainty", "qualifier_certainty"),
        Index("idx_facts_temporality", "temporality"),
    )


class EvidenceDB(Base):
    """Preuve textuelle pour un code."""

    __tablename__ = "evidences"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    code_id: Mapped[int] = mapped_column(
        Integer, ForeignKey("codes.id", ondelete="CASCADE"), nullable=False, index=True
    )
    document_id: Mapped[int] = mapped_column(
        Integer, ForeignKey("clinical_documents.id", ondelete="CASCADE"), nullable=False
    )
    span_start: Mapped[int] = mapped_column(Integer, nullable=False)
    span_end: Mapped[int] = mapped_column(Integer, nullable=False)
    text: Mapped[str] = mapped_column(Text, nullable=False)
    context: Mapped[str | None] = mapped_column(Text, nullable=True)
    created_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow
    )

    # Relations
    code: Mapped["CodeDB"] = relationship("CodeDB", back_populates="evidences")
    document: Mapped["ClinicalDocumentDB"] = relationship(
        "ClinicalDocumentDB", back_populates="evidences"
    )

    __table_args__ = (Index("idx_evidences_code_id", "code_id"),)


class CodeDB(Base):
    """Code médical proposé (CIM-10 ou CCAM)."""

    __tablename__ = "codes"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    stay_id: Mapped[int] = mapped_column(
        Integer, ForeignKey("stays.id", ondelete="CASCADE"), nullable=False, index=True
    )
    code: Mapped[str] = mapped_column(String(50), nullable=False, index=True)
    label: Mapped[str] = mapped_column(String(500), nullable=False)
    type: Mapped[str] = mapped_column(String(10), nullable=False, index=True)
    confidence: Mapped[float] = mapped_column(Float, nullable=False)
    reasoning: Mapped[str] = mapped_column(Text, nullable=False)
    referentiel_version: Mapped[str] = mapped_column(String(50), nullable=False, index=True)
    
    # Statut du code
    status: Mapped[str] = mapped_column(
        String(20), nullable=False, default="proposed", index=True
    )  # proposed, accepted, rejected, corrected
    
    # Métadonnées de versionnement
    model_name: Mapped[str] = mapped_column(String(100), nullable=False)
    model_digest: Mapped[str] = mapped_column(String(64), nullable=False)
    prompt_version: Mapped[str] = mapped_column(String(50), nullable=False)
    
    # Date CCAM (obligatoire depuis 2026)
    ccam_realization_date: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
    
    created_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow
    )
    updated_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow, onupdate=datetime.utcnow
    )

    # Relations
    stay: Mapped["StayDB"] = relationship("StayDB", back_populates="codes")
    evidences: Mapped[List["EvidenceDB"]] = relationship(
        "EvidenceDB", back_populates="code", cascade="all, delete-orphan"
    )
    corrections: Mapped[List["TIMCorrectionDB"]] = relationship(
        "TIMCorrectionDB", back_populates="original_code", cascade="all, delete-orphan"
    )

    __table_args__ = (
        Index("idx_codes_stay_id", "stay_id"),
        Index("idx_codes_code", "code"),
        Index("idx_codes_type", "type"),
        Index("idx_codes_status", "status"),
        Index("idx_codes_referentiel_version", "referentiel_version"),
    )


class ValidationIssueDB(Base):
    """Problème de validation détecté."""

    __tablename__ = "validation_issues"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    issue_id: Mapped[str] = mapped_column(String(100), unique=True, nullable=False, index=True)
    stay_id: Mapped[int] = mapped_column(
        Integer, ForeignKey("stays.id", ondelete="CASCADE"), nullable=False, index=True
    )
    severity: Mapped[str] = mapped_column(String(20), nullable=False, index=True)
    category: Mapped[str] = mapped_column(String(50), nullable=False, index=True)
    message: Mapped[str] = mapped_column(Text, nullable=False)
    affected_codes: Mapped[dict] = mapped_column(JSON, nullable=False, default=list)
    suggested_action: Mapped[str] = mapped_column(Text, nullable=False)
    resolved: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, index=True)
    created_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow
    )

    # Relations
    stay: Mapped["StayDB"] = relationship("StayDB", back_populates="validation_issues")

    __table_args__ = (
        Index("idx_issues_stay_id", "stay_id"),
        Index("idx_issues_severity", "severity"),
        Index("idx_issues_category", "category"),
        Index("idx_issues_resolved", "resolved"),
    )


class QuestionDB(Base):
    """Question générée pour informations manquantes."""

    __tablename__ = "questions"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    question_id: Mapped[str] = mapped_column(String(100), unique=True, nullable=False, index=True)
    stay_id: Mapped[int] = mapped_column(
        Integer, ForeignKey("stays.id", ondelete="CASCADE"), nullable=False, index=True
    )
    text: Mapped[str] = mapped_column(Text, nullable=False)
    priority: Mapped[int] = mapped_column(Integer, nullable=False, index=True)
    category: Mapped[str] = mapped_column(String(50), nullable=False, index=True)
    context: Mapped[str] = mapped_column(Text, nullable=False)
    suggested_answers: Mapped[dict] = mapped_column(JSON, nullable=False, default=list)
    answered: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, index=True)
    answer: Mapped[str | None] = mapped_column(Text, nullable=True)
    created_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow
    )

    # Relations
    stay: Mapped["StayDB"] = relationship("StayDB", back_populates="questions")

    __table_args__ = (
        Index("idx_questions_stay_id", "stay_id"),
        Index("idx_questions_priority", "priority"),
        Index("idx_questions_answered", "answered"),
    )


class AuditRecordDB(Base):
    """Enregistrement d'audit pour traçabilité."""

    __tablename__ = "audit_records"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    record_id: Mapped[str] = mapped_column(String(100), unique=True, nullable=False, index=True)
    stay_id: Mapped[int] = mapped_column(
        Integer, ForeignKey("stays.id", ondelete="CASCADE"), nullable=False, index=True
    )
    timestamp: Mapped[datetime] = mapped_column(DateTime, nullable=False, index=True)
    event_type: Mapped[str] = mapped_column(String(50), nullable=False, index=True)
    actor: Mapped[str] = mapped_column(String(100), nullable=False, index=True)
    data: Mapped[dict] = mapped_column(JSON, nullable=False, default=dict)
    
    # Informations de version (dénormalisé pour performance)
    model_name: Mapped[str] = mapped_column(String(100), nullable=False)
    model_digest: Mapped[str] = mapped_column(String(64), nullable=False)
    prompt_version: Mapped[str] = mapped_column(String(50), nullable=False)
    prompt_hash: Mapped[str] = mapped_column(String(64), nullable=False)
    rules_version: Mapped[str] = mapped_column(String(50), nullable=False)
    rules_hash: Mapped[str] = mapped_column(String(64), nullable=False)
    groupage_version: Mapped[str | None] = mapped_column(String(50), nullable=True)
    referentiels_versions: Mapped[dict] = mapped_column(JSON, nullable=False, default=dict)
    inference_params: Mapped[dict] = mapped_column(JSON, nullable=False, default=dict)
    
    created_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow
    )

    # Relations
    stay: Mapped["StayDB"] = relationship("StayDB", back_populates="audit_records")

    __table_args__ = (
        Index("idx_audit_stay_id", "stay_id"),
        Index("idx_audit_timestamp", "timestamp"),
        Index("idx_audit_event_type", "event_type"),
        Index("idx_audit_actor", "actor"),
    )


class ReferentielVersionDB(Base):
    """Version d'un référentiel ATIH."""

    __tablename__ = "referentiel_versions"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    type: Mapped[str] = mapped_column(String(20), nullable=False, index=True)
    version: Mapped[str] = mapped_column(String(50), nullable=False, index=True)
    import_date: Mapped[datetime] = mapped_column(DateTime, nullable=False)
    file_hash: Mapped[str] = mapped_column(String(64), nullable=False, unique=True)
    chunk_count: Mapped[int] = mapped_column(Integer, nullable=False)
    index_hash: Mapped[str] = mapped_column(String(64), nullable=False)
    active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True, index=True)
    created_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow
    )

    __table_args__ = (
        UniqueConstraint("type", "version", name="uq_referentiel_type_version"),
        Index("idx_referentiel_type", "type"),
        Index("idx_referentiel_version", "version"),
        Index("idx_referentiel_active", "active"),
    )


class TIMCorrectionDB(Base):
    """Correction apportée par un TIM."""

    __tablename__ = "tim_corrections"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    original_code_id: Mapped[int] = mapped_column(
        Integer, ForeignKey("codes.id", ondelete="CASCADE"), nullable=False, index=True
    )
    corrected_code: Mapped[str] = mapped_column(String(50), nullable=False)
    corrected_label: Mapped[str] = mapped_column(String(500), nullable=False)
    corrected_type: Mapped[str] = mapped_column(String(10), nullable=False)
    user_id: Mapped[str] = mapped_column(String(100), nullable=False, index=True)
    comment: Mapped[str | None] = mapped_column(Text, nullable=True)
    timestamp: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow, index=True
    )

    # Relations
    original_code: Mapped["CodeDB"] = relationship("CodeDB", back_populates="corrections")

    __table_args__ = (
        Index("idx_corrections_original_code_id", "original_code_id"),
        Index("idx_corrections_user_id", "user_id"),
        Index("idx_corrections_timestamp", "timestamp"),
    )


class VerificationResultDB(Base):
    """Résultat de la vérification indépendante."""

    __tablename__ = "verification_results"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    stay_id: Mapped[int] = mapped_column(
        Integer, ForeignKey("stays.id", ondelete="CASCADE"), nullable=False, index=True
    )
    decision: Mapped[str] = mapped_column(String(20), nullable=False, index=True)
    dim_errors: Mapped[dict] = mapped_column(JSON, nullable=False, default=list)
    contradictions: Mapped[dict] = mapped_column(JSON, nullable=False, default=list)
    alternatives: Mapped[dict] = mapped_column(JSON, nullable=False, default=list)
    reasoning: Mapped[str] = mapped_column(Text, nullable=False)
    
    # Métadonnées de versionnement
    model_name: Mapped[str] = mapped_column(String(100), nullable=False)
    model_digest: Mapped[str] = mapped_column(String(64), nullable=False)
    prompt_version: Mapped[str] = mapped_column(String(50), nullable=False)
    
    created_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow
    )

    # Relations
    stay: Mapped["StayDB"] = relationship("StayDB", back_populates="verification_results")

    __table_args__ = (
        Index("idx_verification_stay_id", "stay_id"),
        Index("idx_verification_decision", "decision"),
    )


class GroupageResultDB(Base):
    """Résultat de la validation par fonction de groupage."""

    __tablename__ = "groupage_results"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    stay_id: Mapped[int] = mapped_column(
        Integer, ForeignKey("stays.id", ondelete="CASCADE"), nullable=False, index=True
    )
    ghm: Mapped[str | None] = mapped_column(String(20), nullable=True, index=True)
    ghs: Mapped[str | None] = mapped_column(String(20), nullable=True, index=True)
    groupage_errors: Mapped[dict] = mapped_column(JSON, nullable=False, default=list)
    ccam_date_errors: Mapped[dict] = mapped_column(JSON, nullable=False, default=list)
    groupage_version: Mapped[str] = mapped_column(String(50), nullable=False, index=True)
    groupage_date: Mapped[datetime] = mapped_column(DateTime, nullable=False)
    created_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, default=datetime.utcnow
    )

    # Relations
    stay: Mapped["StayDB"] = relationship("StayDB", back_populates="groupage_results")

    __table_args__ = (
        Index("idx_groupage_stay_id", "stay_id"),
        Index("idx_groupage_ghm", "ghm"),
        Index("idx_groupage_version", "groupage_version"),
    )
