from __future__ import annotations

from datetime import date, datetime, timezone

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

from app.core.db.common import CommonBase


class NoticeCase(CommonBase):
    """Department notice / case / appeal master.

    A case is intentionally kept separate from engagements because GST/Income Tax/ROC
    proceedings can continue across years, have independent due dates, and may later
    be linked to one or more engagements/tasks.
    """

    __tablename__ = "notice_cases"
    __table_args__ = (
        UniqueConstraint("tenant_id", "case_code", name="uq_notice_cases_tenant_code"),
    )

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True)
    branch_id: Mapped[int | None] = mapped_column(ForeignKey("branches.id", ondelete="SET NULL"), nullable=True, index=True)
    client_id: Mapped[int] = mapped_column(ForeignKey("clients.id", ondelete="CASCADE"), nullable=False, index=True)
    engagement_id: Mapped[int | None] = mapped_column(ForeignKey("client_service_subscriptions.id", ondelete="SET NULL"), nullable=True, index=True)

    case_code: Mapped[str] = mapped_column(String(80), nullable=False, index=True)
    department: Mapped[str] = mapped_column(String(40), nullable=False, index=True)
    case_type: Mapped[str] = mapped_column(String(50), nullable=False, index=True)
    title: Mapped[str] = mapped_column(String(255), nullable=False)
    reference_no: Mapped[str | None] = mapped_column(String(150), nullable=True, index=True)
    din_ack_no: Mapped[str | None] = mapped_column(String(150), nullable=True, index=True)

    notice_date: Mapped[date | None] = mapped_column(Date, nullable=True, index=True)
    due_date: Mapped[date | None] = mapped_column(Date, nullable=True, index=True)
    financial_year: Mapped[str | None] = mapped_column(String(9), nullable=True, index=True)
    assessment_year: Mapped[str | None] = mapped_column(String(9), nullable=True, index=True)
    period_label: Mapped[str | None] = mapped_column(String(60), nullable=True, index=True)

    status: Mapped[str] = mapped_column(String(40), nullable=False, default="open", index=True)
    priority: Mapped[str] = mapped_column(String(20), nullable=False, default="normal", index=True)
    issue_summary: Mapped[str | None] = mapped_column(Text, nullable=True)
    remarks: Mapped[str | None] = mapped_column(Text, nullable=True)

    assigned_partner_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True)
    assigned_manager_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True)
    assigned_staff_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True)

    is_archived: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, index=True)
    archived_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    archived_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)

    created_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
    updated_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
    created_at_utc: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False)
    updated_at_utc: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc), nullable=False)

    client = relationship("Client")
    engagement = relationship("ClientServiceSubscription")
    assigned_partner = relationship("User", foreign_keys=[assigned_partner_user_id])
    assigned_manager = relationship("User", foreign_keys=[assigned_manager_user_id])
    assigned_staff = relationship("User", foreign_keys=[assigned_staff_user_id])
    events = relationship("NoticeCaseEvent", back_populates="case", cascade="all, delete-orphan", passive_deletes=True, order_by="NoticeCaseEvent.event_date.desc(), NoticeCaseEvent.id.desc()")
    hearings = relationship("NoticeCaseHearing", back_populates="case", cascade="all, delete-orphan", passive_deletes=True, order_by="NoticeCaseHearing.hearing_date.asc(), NoticeCaseHearing.id.asc()")
    orders = relationship("NoticeCaseOrder", back_populates="case", cascade="all, delete-orphan", passive_deletes=True, order_by="NoticeCaseOrder.order_date.desc(), NoticeCaseOrder.id.desc()")
    documents = relationship("NoticeCaseDocument", back_populates="case", cascade="all, delete-orphan", passive_deletes=True, order_by="NoticeCaseDocument.uploaded_at_utc.desc()")


class NoticeCaseEvent(CommonBase):
    __tablename__ = "notice_case_events"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True)
    branch_id: Mapped[int | None] = mapped_column(ForeignKey("branches.id", ondelete="SET NULL"), nullable=True, index=True)
    case_id: Mapped[int] = mapped_column(ForeignKey("notice_cases.id", ondelete="CASCADE"), nullable=False, index=True)

    event_type: Mapped[str] = mapped_column(String(60), nullable=False, index=True)
    event_date: Mapped[date] = mapped_column(Date, nullable=False, index=True)
    description: Mapped[str] = mapped_column(Text, nullable=False)
    next_due_date: Mapped[date | None] = mapped_column(Date, nullable=True, index=True)
    created_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True)
    created_at_utc: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False)

    case = relationship("NoticeCase", back_populates="events")
    created_by = relationship("User")


class NoticeCaseHearing(CommonBase):
    __tablename__ = "notice_case_hearings"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True)
    branch_id: Mapped[int | None] = mapped_column(ForeignKey("branches.id", ondelete="SET NULL"), nullable=True, index=True)
    case_id: Mapped[int] = mapped_column(ForeignKey("notice_cases.id", ondelete="CASCADE"), nullable=False, index=True)

    hearing_date: Mapped[date] = mapped_column(Date, nullable=False, index=True)
    hearing_time: Mapped[str | None] = mapped_column(String(20), nullable=True)
    venue_or_mode: Mapped[str | None] = mapped_column(String(200), nullable=True)
    officer_name: Mapped[str | None] = mapped_column(String(150), nullable=True)
    agenda: Mapped[str | None] = mapped_column(Text, nullable=True)
    outcome: Mapped[str | None] = mapped_column(Text, nullable=True)
    status: Mapped[str] = mapped_column(String(30), nullable=False, default="scheduled", index=True)

    created_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True)
    created_at_utc: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False)
    updated_at_utc: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc), nullable=False)

    case = relationship("NoticeCase", back_populates="hearings")
    created_by = relationship("User")


class NoticeCaseOrder(CommonBase):
    __tablename__ = "notice_case_orders"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True)
    branch_id: Mapped[int | None] = mapped_column(ForeignKey("branches.id", ondelete="SET NULL"), nullable=True, index=True)
    case_id: Mapped[int] = mapped_column(ForeignKey("notice_cases.id", ondelete="CASCADE"), nullable=False, index=True)

    order_type: Mapped[str] = mapped_column(String(60), nullable=False, index=True)
    order_no: Mapped[str | None] = mapped_column(String(150), nullable=True, index=True)
    order_date: Mapped[date] = mapped_column(Date, nullable=False, index=True)
    demand_amount: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    tax_amount: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    interest_amount: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    penalty_amount: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    summary: Mapped[str | None] = mapped_column(Text, nullable=True)
    appeal_due_date: Mapped[date | None] = mapped_column(Date, nullable=True, index=True)
    appeal_filed: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, index=True)

    created_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True)
    created_at_utc: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False)

    case = relationship("NoticeCase", back_populates="orders")
    created_by = relationship("User")


class NoticeCaseDocument(CommonBase):
    __tablename__ = "notice_case_documents"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    tenant_id: Mapped[int] = mapped_column(ForeignKey("tenants.id", ondelete="CASCADE"), nullable=False, index=True)
    branch_id: Mapped[int | None] = mapped_column(ForeignKey("branches.id", ondelete="SET NULL"), nullable=True, index=True)
    case_id: Mapped[int] = mapped_column(ForeignKey("notice_cases.id", ondelete="CASCADE"), nullable=False, index=True)
    event_id: Mapped[int | None] = mapped_column(ForeignKey("notice_case_events.id", ondelete="SET NULL"), nullable=True, index=True)

    document_type: Mapped[str] = mapped_column(String(80), nullable=False, default="GENERAL", index=True)
    title: Mapped[str] = mapped_column(String(255), nullable=False)
    description: Mapped[str | None] = mapped_column(Text, nullable=True)
    original_filename: Mapped[str] = mapped_column(String(255), nullable=False)
    stored_filename: Mapped[str] = mapped_column(String(255), nullable=False)
    content_type: Mapped[str | None] = mapped_column(String(150), nullable=True)
    file_size_bytes: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    local_relative_path: Mapped[str] = mapped_column(String(1000), nullable=False)
    version_no: Mapped[int] = mapped_column(Integer, nullable=False, default=1)
    status: Mapped[str] = mapped_column(String(30), nullable=False, default="active", index=True)
    is_deleted: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, index=True)
    deleted_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    deleted_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)

    uploaded_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
    uploaded_at_utc: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False)

    case = relationship("NoticeCase", back_populates="documents")
    event = relationship("NoticeCaseEvent")
    uploaded_by = relationship("User", foreign_keys=[uploaded_by_user_id])
