import enum
import uuid as py_uuid
from datetime import date
from typing import Any, Optional, TYPE_CHECKING

from sqlalchemy import (
    Boolean,
    Date,
    Enum,
    ForeignKey,
    Index,
    Integer,
    JSON,
    String,
    Text,
    UniqueConstraint,
)
from sqlalchemy.dialects.postgresql import ARRAY, JSONB, UUID
from sqlalchemy.orm import Mapped, mapped_column, relationship

from prism_models.base import BaseModel

if TYPE_CHECKING:
    from prism_models.chat import Contact
    from prism_models.agent_profile import Profile
    from prism_models.content import Document


class RFPStatus(str, enum.Enum):
    PENDING = "pending"
    PROCESSING = "processing"
    COMPLETED = "completed"
    FAILED = "failed"

class RFPIngestionStatus(str, enum.Enum):
    PENDING = "pending"
    PROCESSING = "processing"
    COMPLETED = "completed"

class RFPMode(str, enum.Enum):
    EXTRACTION_ONLY = "extraction_only"
    EXTRACTION_AND_ANSWER = "extraction_and_answer"


class EmergencyConcernType(str, enum.Enum):
    MEDICAL = "medical"
    SECURITY = "security"
    MEDICAL_AND_SECURITY = "medical_and_security"

class CoverageScope(str, enum.Enum):
    INTERNATIONAL = "international"
    DOMESTIC = "domestic"
    INTERNATIONAL_AND_DOMESTIC = "international_and_domestic"

class ActivityType(str, enum.Enum):
    BUSINESS = "business"
    OPERATIONAL = "operational"
    MISSIONARY = "missionary"
    HUMANITARIAN = "humanitarian"
    ACADEMIC = "academic"
    OTHER = "other"

class RFPProfileStatus(str, enum.Enum):
    DRAFT = "draft"
    IN_PROGRESS = "in_progress"
    COMPLETED = "completed"

class TravelGroupPattern(str, enum.Enum):
    SOLO = "solo"
    SMALL_GROUPS = "small_groups"
    LARGE_GROUPS = "large_groups"
    MIXED = "mixed"


class RFP(BaseModel):
    """RFP model for storing uploaded CSV documents."""

    uploaded_by_contact_id: Mapped[int | None] = mapped_column(
        ForeignKey("contact.id", ondelete="SET NULL"),
        nullable=True,
        index=True,
    )
    profile_id: Mapped[int | None] = mapped_column(
        ForeignKey("profile.id", ondelete="SET NULL"),
        nullable=True,
        index=True,
    )
    rfp_profile_id: Mapped[int | None] = mapped_column(
        ForeignKey("rfp_profile.id", ondelete="SET NULL"),
        nullable=True,
        index=True,
    )
    rfp_profile_ids_to_search: Mapped[list[int] | None] = mapped_column(
        ARRAY(Integer),
        nullable=True,
    )
    rfp_mode: Mapped[RFPMode | None] = mapped_column(
        Enum(
            RFPMode,
            name="rfpmode",
            native_enum=False
        ),
        nullable=True,
    )
    document_id: Mapped[int | None] = mapped_column(
        ForeignKey("document.id", ondelete="SET NULL"),
        nullable=True,
        unique=True,
    )
    s3_url: Mapped[str | None] = mapped_column(String(2048), nullable=True)
    total_questions: Mapped[int] = mapped_column(Integer, nullable=True, default=0)
    answered_questions: Mapped[int] = mapped_column(Integer, nullable=True, default=0)
    status: Mapped[RFPStatus] = mapped_column(
        Enum(RFPStatus, native_enum=False),
        nullable=False,
        index=True,
        default=RFPStatus.PENDING,
    )
    ingestion_status: Mapped[RFPIngestionStatus | None] = mapped_column(
        Enum(RFPIngestionStatus, native_enum=False),
        nullable=True,
        index=True
    )
    additional_data: Mapped[dict[str, Any] | None] = mapped_column(
        "additional_data", JSON, nullable=True, default=dict
    )

    uploaded_by: Mapped[Optional["Contact"]] = relationship("Contact", foreign_keys=[uploaded_by_contact_id])
    profile: Mapped[Optional["Profile"]] = relationship("Profile")
    rfp_profile: Mapped[Optional["RFPProfile"]] = relationship("RFPProfile", back_populates="rfps")
    document: Mapped[Optional["Document"]] = relationship("Document", back_populates="rfp")
    qna_items: Mapped[list["RFPQNA"]] = relationship(
        "RFPQNA",
        back_populates="rfp",
        cascade="all, delete-orphan",
    )


class RFPProfile(BaseModel):
    """Grouping model that holds shared questionnaire data for related RFPs."""

    __table_args__ = (
        UniqueConstraint("enterprise_name", name="uq_rfp_profile_enterprise_name"),
    )

    # --- Status ---
    status: Mapped[RFPProfileStatus | None] = mapped_column(
        Enum(RFPProfileStatus, name="rfpprofilestatus", native_enum=False),
        nullable=True,
        index=True,
        default=RFPProfileStatus.DRAFT,
    )
    is_training_data: Mapped[bool | None] = mapped_column(Boolean, nullable=True, default=False)

    # --- Organization ---
    enterprise_name: Mapped[str | None] = mapped_column(String(255), nullable=True, index=True)
    organization_size: Mapped[int | None] = mapped_column(Integer, nullable=True)
    unique_travelers_annually: Mapped[int | None] = mapped_column(Integer, nullable=True)

    # --- Demographics ---
    traveler_categories: Mapped[list[str] | None] = mapped_column(ARRAY(String), nullable=True)
    expats_overseas: Mapped[int | None] = mapped_column(Integer, nullable=True)
    minor_children: Mapped[int | None] = mapped_column(Integer, nullable=True)
    travelers_75_to_84: Mapped[int | None] = mapped_column(Integer, nullable=True)
    travelers_85_plus: Mapped[int | None] = mapped_column(Integer, nullable=True)

    # --- Travel Patterns ---
    total_annual_travel_days: Mapped[int | None] = mapped_column(Integer, nullable=True)
    typical_trip_length_days: Mapped[int | None] = mapped_column(Integer, nullable=True)
    longest_trip_days: Mapped[int | None] = mapped_column(Integer, nullable=True)
    total_trips_annually: Mapped[int | None] = mapped_column(Integer, nullable=True)
    travel_group_pattern: Mapped[TravelGroupPattern | None] = mapped_column(
        Enum(TravelGroupPattern, name="travelgrouppattern", native_enum=False), nullable=True
    )

    # --- Destinations ---
    typical_destinations: Mapped[list[str] | None] = mapped_column(ARRAY(String), nullable=True)
    includes_remote_rural: Mapped[bool | None] = mapped_column(Boolean, nullable=True)
    activity_type: Mapped[ActivityType | None] = mapped_column(
        Enum(ActivityType, name="activitytype", native_enum=False), nullable=True
    )
    activity_description: Mapped[str | None] = mapped_column(Text, nullable=True)

    # --- Service Needs ---
    emergency_concern_type: Mapped[EmergencyConcernType | None] = mapped_column(
        Enum(EmergencyConcernType, name="emergencyconcerntype", native_enum=False), nullable=True
    )
    coverage_scope: Mapped[CoverageScope | None] = mapped_column(
        Enum(CoverageScope, name="coveragescope", native_enum=False), nullable=True
    )
    mobile_app_required: Mapped[bool | None] = mapped_column(Boolean, nullable=True)
    desktop_tracking_required: Mapped[bool | None] = mapped_column(Boolean, nullable=True)
    business_and_personal: Mapped[bool | None] = mapped_column(Boolean, nullable=True)

    # --- Risk History ---
    incidents_past_5_years: Mapped[int | None] = mapped_column(Integer, nullable=True)
    incidents_past_year: Mapped[int | None] = mapped_column(Integer, nullable=True)
    medical_evacuations_annually: Mapped[int | None] = mapped_column(Integer, nullable=True)
    security_evacuations_annually: Mapped[int | None] = mapped_column(Integer, nullable=True)
    monitoring_staff_count: Mapped[int | None] = mapped_column(Integer, nullable=True)
    has_gsoc: Mapped[bool | None] = mapped_column(Boolean, nullable=True)

    # --- Administrative ---
    current_tmc: Mapped[str | None] = mapped_column(String(255), nullable=True)
    current_duty_of_care_provider: Mapped[str | None] = mapped_column(String(255), nullable=True)
    contract_expiration_date: Mapped[date | None] = mapped_column(Date, nullable=True)
    desired_start_date: Mapped[date | None] = mapped_column(Date, nullable=True)

    # --- Escape hatch ---
    additional_data: Mapped[dict[str, Any] | None] = mapped_column("additional_data", JSON, nullable=True, default=dict)

    # --- Relationships ---
    rfps: Mapped[list["RFP"]] = relationship("RFP", back_populates="rfp_profile")
    reference_links: Mapped[list["RFPProfileReference"]] = relationship(
        "RFPProfileReference",
        foreign_keys="RFPProfileReference.rfp_profile_id",
        back_populates="rfp_profile",
        cascade="all, delete-orphan",
    )


class RFPProfileReference(BaseModel):
    """Junction table linking an RFP profile to other RFP profiles
    whose Q&A history should be used as reference when answering questions."""

    __table_args__ = (
        UniqueConstraint(
            "rfp_profile_id",
            "reference_rfp_profile_id",
            name="uq_rfp_profile_reference_pair",
        ),
        Index("ix_rfp_profile_ref_profile_id", "rfp_profile_id"),
        Index("ix_rfp_profile_ref_reference_id", "reference_rfp_profile_id"),
    )

    rfp_profile_id: Mapped[int] = mapped_column(
        ForeignKey("rfp_profile.id", ondelete="CASCADE"),
        nullable=False,
    )
    reference_rfp_profile_id: Mapped[int] = mapped_column(
        ForeignKey("rfp_profile.id", ondelete="CASCADE"),
        nullable=False,
    )

    rfp_profile: Mapped["RFPProfile"] = relationship(
        "RFPProfile",
        foreign_keys=[rfp_profile_id],
        back_populates="reference_links",
    )
    reference_rfp_profile: Mapped["RFPProfile"] = relationship(
        "RFPProfile",
        foreign_keys=[reference_rfp_profile_id],
    )


class RFPQNA(BaseModel):
    """Q&A entries generated for a specific RFP."""

    rfp_id: Mapped[int] = mapped_column(
        ForeignKey("rfp.id", ondelete="CASCADE"),
        nullable=False,
        index=True,
    )
    uuid: Mapped[py_uuid.UUID | None] = mapped_column(
        UUID(as_uuid=True),
        default=py_uuid.uuid4,
        nullable=True,
        unique=True,
        index=True,
    )
    question: Mapped[str] = mapped_column(Text, nullable=False)
    question_type: Mapped[str | None] = mapped_column(String(255), nullable=True)
    relevant_department: Mapped[str | None] = mapped_column(String(255), nullable=True)
    parent_question_id: Mapped[int | None] = mapped_column(Integer, nullable=True)
    workbook_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
    answer: Mapped[str | None] = mapped_column(Text, nullable=True)
    human_response: Mapped[str | None] = mapped_column(Text, nullable=True)
    confidence: Mapped[str | None] = mapped_column(String(50), nullable=True)
    citations: Mapped[list[dict[str, Any]] | None] = mapped_column(JSONB, nullable=True)
    raw_llm_response: Mapped[dict[str, Any] | None] = mapped_column(JSONB, nullable=True)

    rfp: Mapped["RFP"] = relationship("RFP", back_populates="qna_items")
