from __future__ import annotations

import enum
from datetime import date as DateType, datetime as DateTimeType

from sqlalchemy import Boolean, Date, DateTime, Enum, ForeignKey, Integer, JSON, String, Text, func
from sqlalchemy.orm import Mapped, mapped_column, relationship

from app.database import Base


def enum_values(enum_cls: type[enum.Enum]) -> Enum:
    return Enum(enum_cls, values_callable=lambda members: [member.value for member in members], native_enum=False)


class UserRole(str, enum.Enum):
    admin = "admin"
    reviewer = "reviewer"
    operator = "operator"


class ProcessStatus(str, enum.Enum):
    unprocessed = "unprocessed"
    processing = "processing"
    parsed = "parsed"
    pending_review = "pending_review"
    completed = "completed"
    failed = "failed"


class RiskLevel(str, enum.Enum):
    low = "low"
    medium = "medium"
    high = "high"
    forbidden = "forbidden"


class ReviewStatus(str, enum.Enum):
    pending = "pending"
    reviewing = "reviewing"
    approved = "approved"
    needs_revision = "needs_revision"
    rejected = "rejected"
    forbidden = "forbidden"


class DesensitizationStatus(str, enum.Enum):
    not_needed = "not_needed"
    needed = "needed"
    done = "done"
    unknown = "unknown"


class CourseStage(str, enum.Enum):
    awareness = "觉知"
    origin = "原生"
    unity = "合一"
    vitality = "生机"
    heart_light = "心光"
    camp_general = "大本营综合"
    unknown = "不确定"


class PrimaryTopic(str, enum.Enum):
    parent_child = "亲子关系"
    marriage = "婚姻关系"
    family_of_origin = "原生家庭"
    emotion = "情绪模式"
    course = "课程学习"
    growth = "个人成长"
    family_system = "家族系统"
    relationship_drain = "关系内耗"
    action = "行动力与拖延"
    other = "其他"


class AuditStatus(str, enum.Enum):
    draft = "draft"
    pending = "pending"
    approved = "approved"
    needs_revision = "needs_revision"
    disabled = "disabled"


class CallStatus(str, enum.Enum):
    callable = "callable"
    not_callable = "not_callable"
    need_review = "need_review"
    forbidden = "forbidden"


class TaskType(str, enum.Enum):
    scheduled_scan = "scheduled_scan"
    manual_scan = "manual_scan"
    manual_process = "manual_process"
    ai_cleaning = "ai_cleaning"
    feishu_sync = "feishu_sync"


class TaskStatus(str, enum.Enum):
    pending = "pending"
    running = "running"
    success = "success"
    failed = "failed"
    partial_success = "partial_success"


class TargetType(str, enum.Enum):
    raw_qa = "raw_qa"
    standard_qa = "standard_qa"
    session = "session"


class AuditAction(str, enum.Enum):
    approve = "approve"
    revise = "revise"
    reject = "reject"
    forbid = "forbid"
    mark_high_risk = "mark_high_risk"
    mark_desensitization = "mark_desensitization"
    mark_callable = "mark_callable"
    disable = "disable"


class TimestampMixin:
    created_at: Mapped[DateTimeType] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False)
    updated_at: Mapped[DateTimeType] = mapped_column(
        DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), nullable=False
    )


class User(Base, TimestampMixin):
    __tablename__ = "users"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
    name: Mapped[str] = mapped_column(String(120), nullable=False)
    email: Mapped[str] = mapped_column(String(255), unique=True, index=True, nullable=False)
    role: Mapped[UserRole] = mapped_column(enum_values(UserRole), default=UserRole.reviewer, nullable=False)


class FeishuSession(Base, TimestampMixin):
    __tablename__ = "feishu_sessions"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
    session_code: Mapped[str] = mapped_column(String(64), unique=True, index=True, nullable=False)
    title: Mapped[str] = mapped_column(String(255), nullable=False)
    date: Mapped[DateType | None] = mapped_column(Date, nullable=True)
    teachers: Mapped[str | None] = mapped_column(String(255), nullable=True)
    feishu_doc_url: Mapped[str | None] = mapped_column(String(1024), nullable=True)
    feishu_record_id: Mapped[str | None] = mapped_column(String(128), nullable=True)
    source_file_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
    source_text: Mapped[str | None] = mapped_column(Text, nullable=True)
    process_status: Mapped[ProcessStatus] = mapped_column(
        enum_values(ProcessStatus), default=ProcessStatus.unprocessed, nullable=False, index=True
    )
    qa_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    pending_review_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    approved_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    standard_qa_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    failed_reason: Mapped[str | None] = mapped_column(Text, nullable=True)

    raw_items: Mapped[list["RawQAItem"]] = relationship(back_populates="session", cascade="all, delete-orphan")
    standard_items: Mapped[list["StandardQAItem"]] = relationship(back_populates="session")


class RawQAItem(Base, TimestampMixin):
    __tablename__ = "raw_qa_items"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
    session_id: Mapped[int] = mapped_column(ForeignKey("feishu_sessions.id"), index=True, nullable=False)
    qa_code: Mapped[str] = mapped_column(String(64), unique=True, index=True, nullable=False)
    raw_question: Mapped[str] = mapped_column(Text, nullable=False)
    normalized_question: Mapped[str | None] = mapped_column(Text, nullable=True)
    raw_answer: Mapped[str] = mapped_column(Text, nullable=False)
    normalized_answer: Mapped[str | None] = mapped_column(Text, nullable=True)
    suggested_standard_question: Mapped[str | None] = mapped_column(Text, nullable=True)
    suggested_standard_answer: Mapped[str | None] = mapped_column(Text, nullable=True)
    answer_person: Mapped[str | None] = mapped_column(String(120), nullable=True)
    primary_topic: Mapped[PrimaryTopic] = mapped_column(enum_values(PrimaryTopic), default=PrimaryTopic.other, nullable=False)
    problem_tags: Mapped[list[str]] = mapped_column(JSON, default=list, nullable=False)
    course_stage: Mapped[CourseStage] = mapped_column(enum_values(CourseStage), default=CourseStage.unknown, nullable=False)
    audience_tags: Mapped[list[str]] = mapped_column(JSON, default=list, nullable=False)
    emotion_intensity: Mapped[str | None] = mapped_column(String(32), nullable=True)
    risk_level: Mapped[RiskLevel] = mapped_column(enum_values(RiskLevel), default=RiskLevel.medium, nullable=False, index=True)
    risk_types: Mapped[list[str]] = mapped_column(JSON, default=list, nullable=False)
    risk_notes: Mapped[str | None] = mapped_column(Text, nullable=True)
    need_desensitization: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    desensitization_status: Mapped[DesensitizationStatus] = mapped_column(
        enum_values(DesensitizationStatus), default=DesensitizationStatus.unknown, nullable=False
    )
    review_status: Mapped[ReviewStatus] = mapped_column(
        enum_values(ReviewStatus), default=ReviewStatus.pending, nullable=False, index=True
    )
    reviewer_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True)
    review_notes: Mapped[str | None] = mapped_column(Text, nullable=True)
    suggested_to_standard_qa: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
    source_timestamp: Mapped[str | None] = mapped_column(String(64), nullable=True)

    session: Mapped[FeishuSession] = relationship(back_populates="raw_items")
    reviewer: Mapped[User | None] = relationship()
    standard_item: Mapped["StandardQAItem | None"] = relationship(back_populates="source_raw_qa", uselist=False)


class StandardQAItem(Base, TimestampMixin):
    __tablename__ = "standard_qa_items"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
    standard_code: Mapped[str] = mapped_column(String(64), unique=True, index=True, nullable=False)
    source_raw_qa_id: Mapped[int] = mapped_column(ForeignKey("raw_qa_items.id"), index=True, nullable=False)
    session_id: Mapped[int] = mapped_column(ForeignKey("feishu_sessions.id"), index=True, nullable=False)
    standard_question: Mapped[str] = mapped_column(Text, nullable=False)
    standard_answer: Mapped[str] = mapped_column(Text, nullable=False)
    similar_questions: Mapped[list[str]] = mapped_column(JSON, default=list, nullable=False)
    primary_topic: Mapped[PrimaryTopic] = mapped_column(enum_values(PrimaryTopic), default=PrimaryTopic.other, nullable=False)
    problem_tags: Mapped[list[str]] = mapped_column(JSON, default=list, nullable=False)
    course_stage: Mapped[CourseStage] = mapped_column(enum_values(CourseStage), default=CourseStage.unknown, nullable=False)
    audience_tags: Mapped[list[str]] = mapped_column(JSON, default=list, nullable=False)
    answer_boundary: Mapped[str | None] = mapped_column(Text, nullable=True)
    forbidden_expressions: Mapped[list[str]] = mapped_column(JSON, default=list, nullable=False)
    risk_level: Mapped[RiskLevel] = mapped_column(enum_values(RiskLevel), default=RiskLevel.medium, nullable=False, index=True)
    audit_status: Mapped[AuditStatus] = mapped_column(enum_values(AuditStatus), default=AuditStatus.approved, nullable=False)
    call_status: Mapped[CallStatus] = mapped_column(enum_values(CallStatus), default=CallStatus.not_callable, nullable=False)
    last_reviewer_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True)
    disabled_reason: Mapped[str | None] = mapped_column(Text, nullable=True)

    source_raw_qa: Mapped[RawQAItem] = relationship(back_populates="standard_item")
    session: Mapped[FeishuSession] = relationship(back_populates="standard_items")
    last_reviewer: Mapped[User | None] = relationship()


class TaskRun(Base):
    __tablename__ = "task_runs"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
    task_type: Mapped[TaskType] = mapped_column(enum_values(TaskType), nullable=False, index=True)
    task_status: Mapped[TaskStatus] = mapped_column(enum_values(TaskStatus), default=TaskStatus.pending, nullable=False, index=True)
    started_at: Mapped[DateTimeType | None] = mapped_column(DateTime(timezone=True), nullable=True)
    ended_at: Mapped[DateTimeType | None] = mapped_column(DateTime(timezone=True), nullable=True)
    sessions_scanned: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    sessions_processed: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    qa_created: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    qa_failed: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    error_message: Mapped[str | None] = mapped_column(Text, nullable=True)
    retry_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    created_at: Mapped[DateTimeType] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False)


class AuditLog(Base):
    __tablename__ = "audit_logs"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
    user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True)
    target_type: Mapped[TargetType] = mapped_column(enum_values(TargetType), nullable=False, index=True)
    target_id: Mapped[int] = mapped_column(Integer, index=True, nullable=False)
    action: Mapped[AuditAction] = mapped_column(enum_values(AuditAction), nullable=False)
    before_status: Mapped[str | None] = mapped_column(String(64), nullable=True)
    after_status: Mapped[str | None] = mapped_column(String(64), nullable=True)
    comment: Mapped[str | None] = mapped_column(Text, nullable=True)
    created_at: Mapped[DateTimeType] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False)

    user: Mapped[User | None] = relationship()


class SystemSetting(Base, TimestampMixin):
    __tablename__ = "system_settings"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
    key: Mapped[str] = mapped_column(String(128), unique=True, index=True, nullable=False)
    value: Mapped[str | None] = mapped_column(Text, nullable=True)
    description: Mapped[str | None] = mapped_column(Text, nullable=True)
