from __future__ import annotations

from datetime import date, datetime, timezone

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

from app.core.db.common import CommonBase


class ServiceCategory(CommonBase):
    __tablename__ = "service_categories"
    __table_args__ = (
        UniqueConstraint("code", name="uq_service_categories_code"),
        UniqueConstraint("name", name="uq_service_categories_name"),
    )

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    code: Mapped[str] = mapped_column(String(50), nullable=False, index=True)
    name: Mapped[str] = mapped_column(String(100), nullable=False, index=True)
    sort_order: Mapped[int] = mapped_column(Integer, nullable=False, default=100)
    is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)

    catalogue_items = relationship("ServiceCatalogue", back_populates="service_category")


class ServiceCatalogue(CommonBase):
    __tablename__ = "service_catalogues"
    __table_args__ = (
        UniqueConstraint("service_code", name="uq_service_catalogues_code"),
    )

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    service_code: Mapped[str] = mapped_column(String(50), nullable=False, index=True)
    service_name: Mapped[str] = mapped_column(String(200), nullable=False, index=True)
    category: Mapped[str | None] = mapped_column(String(100), nullable=True)
    category_id: Mapped[int | None] = mapped_column(ForeignKey("service_categories.id"), nullable=True, index=True)
    recurrence_type: Mapped[str | None] = mapped_column(String(30), nullable=True, index=True)
    engagement_type: Mapped[str] = mapped_column(String(20), nullable=False, default="non_assurance", index=True)
    service_scope_type: Mapped[str] = mapped_column(String(30), nullable=False, default="client", server_default="client", index=True)
    required_registration_type: Mapped[str | None] = mapped_column(String(40), nullable=True, index=True)
    sort_order: Mapped[int] = mapped_column(Integer, nullable=False, default=100)
    description: Mapped[str | None] = mapped_column(Text, nullable=True)

    applicable_individual: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    applicable_proprietorship: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    applicable_partnership: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    applicable_llp: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    applicable_company: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    applicable_trust: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    applicable_society: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)

    is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
    is_client_requestable: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    is_consultant_requestable: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)

    created_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True)
    updated_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), 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,
    )

    service_category = relationship("ServiceCategory", back_populates="catalogue_items")
    firm_services = relationship(
        "FirmServiceSelection",
        back_populates="catalogue",
        cascade="all, delete-orphan",
        passive_deletes=True,
    )
    task_templates = relationship(
        "FirmServiceTaskTemplate",
        back_populates="catalogue",
        cascade="all, delete-orphan",
        passive_deletes=True,
    )
    default_task_templates = relationship(
        "ServiceDefaultTaskTemplate",
        back_populates="catalogue",
        cascade="all, delete-orphan",
        passive_deletes=True,
        order_by="ServiceDefaultTaskTemplate.sequence_no.asc()",
    )

    due_date_rules = relationship(
        "ServiceDueDateRule",
        back_populates="catalogue",
        cascade="all, delete-orphan",
        passive_deletes=True,
        order_by="ServiceDueDateRule.sort_order.asc()",
    )


class ServiceDefaultTaskTemplate(CommonBase):
    __tablename__ = "service_default_task_templates"
    __table_args__ = (
        UniqueConstraint("service_catalogue_id", "sequence_no", name="uq_service_default_task_templates_sequence"),
    )

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    service_catalogue_id: Mapped[int] = mapped_column(
        ForeignKey("service_catalogues.id", ondelete="CASCADE"), nullable=False, index=True
    )
    task_name: Mapped[str] = mapped_column(String(200), nullable=False)
    description: Mapped[str | None] = mapped_column(Text, nullable=True)
    sequence_no: Mapped[int] = mapped_column(Integer, nullable=False, default=1)
    default_role_name: Mapped[str | None] = mapped_column(String(100), nullable=True)
    eligible_role_names: Mapped[str | None] = mapped_column(String(200), nullable=True)
    is_mandatory: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
    requires_review: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)

    # AQMM task tags. These flags allow the existing service checklist to become
    # the engagement quality checklist for assurance engagements, without creating
    # a separate duplicate AQMM checklist module.
    is_aqmm_task: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False, index=True)
    aqmm_mandatory: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    aqmm_evidence_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    aqmm_manager_review_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    aqmm_partner_review_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    aqmm_review_partner_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    aqmm_blocks_final_release: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    aqmm_reference: Mapped[str | None] = mapped_column(String(120), nullable=True)

    is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)

    catalogue = relationship("ServiceCatalogue", back_populates="default_task_templates")


class FirmServiceSelection(CommonBase):
    __tablename__ = "firm_service_selections"
    __table_args__ = (
        UniqueConstraint("tenant_id", "service_catalogue_id", name="uq_firm_service_selections_tenant_catalogue"),
    )

    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)
    service_catalogue_id: Mapped[int] = mapped_column(
        ForeignKey("service_catalogues.id", ondelete="CASCADE"), nullable=False, index=True
    )
    is_enabled: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
    default_branch_id: Mapped[int | None] = mapped_column(ForeignKey("branches.id"), nullable=True, index=True)
    activated_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True)
    updated_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True)
    activated_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,
    )

    catalogue = relationship("ServiceCatalogue", back_populates="firm_services")


class FirmServiceTaskTemplate(CommonBase):
    __tablename__ = "firm_service_task_templates"
    __table_args__ = (
        UniqueConstraint(
            "tenant_id", "service_catalogue_id", "sequence_no",
            name="uq_firm_service_task_templates_sequence",
        ),
    )

    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)
    service_catalogue_id: Mapped[int] = mapped_column(
        ForeignKey("service_catalogues.id", ondelete="CASCADE"), nullable=False, index=True
    )

    task_name: Mapped[str] = mapped_column(String(200), nullable=False)
    description: Mapped[str | None] = mapped_column(Text, nullable=True)
    sequence_no: Mapped[int] = mapped_column(Integer, nullable=False, default=1)

    default_role_name: Mapped[str | None] = mapped_column(String(100), nullable=True)
    eligible_role_names: Mapped[str | None] = mapped_column(String(200), nullable=True)
    is_mandatory: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
    requires_review: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)

    # AQMM task tags copied into generated ClientServiceTaskInstance rows.
    is_aqmm_task: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False, index=True)
    aqmm_mandatory: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    aqmm_evidence_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    aqmm_manager_review_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    aqmm_partner_review_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    aqmm_review_partner_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    aqmm_blocks_final_release: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    aqmm_reference: Mapped[str | None] = mapped_column(String(120), nullable=True)

    is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)

    created_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True)
    updated_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), 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,
    )

    catalogue = relationship("ServiceCatalogue", back_populates="task_templates")
    document_requirements = relationship(
        "FirmTaskDocumentRequirement",
        back_populates="task_template",
        cascade="all, delete-orphan",
        passive_deletes=True,
        order_by="FirmTaskDocumentRequirement.sort_order.asc()",
    )
    document_templates = relationship(
        "FirmTaskDocumentTemplate",
        back_populates="task_template",
        cascade="all, delete-orphan",
        passive_deletes=True,
        order_by="FirmTaskDocumentTemplate.uploaded_at_utc.desc()",
    )


class FirmTaskDocumentRequirement(CommonBase):
    """Document required at service task-template level.

    These rows define what must/should be collected when engagement task
    instances are generated from a firm task template. Actual uploaded files are
    linked to ClientServiceTaskInstance through EngagementDocument.task_instance_id.
    """

    __tablename__ = "firm_task_document_requirements"
    __table_args__ = (
        UniqueConstraint(
            "tenant_id",
            "firm_task_template_id",
            "document_name",
            name="uq_firm_task_document_requirements_name",
        ),
    )

    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)
    service_catalogue_id: Mapped[int] = mapped_column(ForeignKey("service_catalogues.id", ondelete="CASCADE"), nullable=False, index=True)
    firm_task_template_id: Mapped[int] = mapped_column(ForeignKey("firm_service_task_templates.id", ondelete="CASCADE"), nullable=False, index=True)

    document_name: Mapped[str] = mapped_column(String(200), nullable=False)
    document_type: Mapped[str] = mapped_column(String(80), nullable=False, default="GENERAL", index=True)
    is_mandatory: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
    allowed_file_types: Mapped[str | None] = mapped_column(String(255), nullable=True)
    instructions: Mapped[str | None] = mapped_column(Text, nullable=True)
    sort_order: Mapped[int] = mapped_column(Integer, nullable=False, default=100)
    is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)

    created_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True)
    updated_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), 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)

    task_template = relationship("FirmServiceTaskTemplate", back_populates="document_requirements")
    catalogue = relationship("ServiceCatalogue")


class FirmTaskDocumentTemplate(CommonBase):
    """Reusable uploaded template file attached to a firm task template.

    Example: GST registration NOC format, partnership deed format, agreement
    draft, company incorporation checklist, board resolution format etc.
    """

    __tablename__ = "firm_task_document_templates"

    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)
    service_catalogue_id: Mapped[int] = mapped_column(ForeignKey("service_catalogues.id", ondelete="CASCADE"), nullable=False, index=True)
    firm_task_template_id: Mapped[int] = mapped_column(ForeignKey("firm_service_task_templates.id", ondelete="CASCADE"), nullable=False, index=True)

    template_name: Mapped[str] = mapped_column(String(200), nullable=False)
    template_category: Mapped[str | None] = mapped_column(String(100), nullable=True, index=True)
    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)
    is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False, index=True)

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

    task_template = relationship("FirmServiceTaskTemplate", back_populates="document_templates")
    catalogue = relationship("ServiceCatalogue")


class ServiceDueDateRule(CommonBase):
    """Statutory/compliance due-date rule attached to a service catalogue item."""

    __tablename__ = "service_due_date_rules"
    __table_args__ = (
        UniqueConstraint("service_catalogue_id", "rule_name", name="uq_service_due_date_rules_catalogue_name"),
    )

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    service_catalogue_id: Mapped[int] = mapped_column(
        ForeignKey("service_catalogues.id", ondelete="CASCADE"), nullable=False, index=True
    )
    rule_name: Mapped[str] = mapped_column(String(150), nullable=False)
    period_type: Mapped[str] = mapped_column(String(30), nullable=False, default="yearly", index=True)
    due_year_basis: Mapped[str] = mapped_column(String(40), nullable=False, default="assessment_year_start")
    due_day: Mapped[int | None] = mapped_column(Integer, nullable=True)
    due_month: Mapped[int | None] = mapped_column(Integer, nullable=True)
    due_month_offset: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
    days_offset_after_event: Mapped[int | None] = mapped_column(Integer, nullable=True)
    renewal_days_before_expiry: Mapped[int | None] = mapped_column(Integer, nullable=True)
    sort_order: Mapped[int] = mapped_column(Integer, nullable=False, default=100)
    is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False, index=True)
    remarks: Mapped[str | None] = mapped_column(Text, nullable=True)

    created_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True)
    updated_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), 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,
    )

    catalogue = relationship("ServiceCatalogue", back_populates="due_date_rules")


class ServiceDueDateExtension(CommonBase):
    """History of statutory due-date extensions for a service/rule/FY/period."""

    __tablename__ = "service_due_date_extensions"

    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)
    service_catalogue_id: Mapped[int] = mapped_column(
        ForeignKey("service_catalogues.id", ondelete="CASCADE"), nullable=False, index=True
    )
    due_date_rule_id: Mapped[int | None] = mapped_column(
        ForeignKey("service_due_date_rules.id", ondelete="SET NULL"), nullable=True, index=True
    )
    financial_year: Mapped[str] = mapped_column(String(9), nullable=False, index=True)
    assessment_year: Mapped[str | None] = mapped_column(String(9), nullable=True, index=True)
    period_label: Mapped[str | None] = mapped_column(String(30), nullable=True, index=True)
    previous_due_date: Mapped[date | None] = mapped_column(Date, nullable=True)
    extended_due_date: Mapped[date] = mapped_column(Date, nullable=False, index=True)
    extension_sequence: Mapped[int] = mapped_column(Integer, nullable=False, default=1)
    notification_reference: Mapped[str | None] = mapped_column(String(200), nullable=True)
    notification_date: Mapped[date | None] = mapped_column(Date, nullable=True)
    remarks: Mapped[str | None] = mapped_column(Text, nullable=True)

    created_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True)
    updated_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), 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,
    )

    catalogue = relationship("ServiceCatalogue")
    due_rule = relationship("ServiceDueDateRule")


class ClientServicePlan(CommonBase):
    """Persistent client-level service subscription from which period engagements are created."""

    __tablename__ = "client_service_plans"
    __table_args__ = (
        UniqueConstraint(
            "tenant_id", "service_catalogue_id", "scope_key",
            name="uq_csp_tenant_service_scope",
        ),
    )

    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)
    scope_type: Mapped[str] = mapped_column(String(30), nullable=False, default="client", server_default="client", index=True)
    scope_key: Mapped[str] = mapped_column(String(80), nullable=False, default="", server_default="", index=True)
    business_unit_id: Mapped[int | None] = mapped_column(ForeignKey("client_business_units.id", ondelete="SET NULL"), nullable=True, index=True)
    client_branch_id: Mapped[int | None] = mapped_column(ForeignKey("client_branches.id", ondelete="SET NULL"), nullable=True, index=True)
    registration_id: Mapped[int | None] = mapped_column(ForeignKey("client_registrations.id", ondelete="SET NULL"), nullable=True, index=True)
    service_catalogue_id: Mapped[int] = mapped_column(ForeignKey("service_catalogues.id", ondelete="CASCADE"), nullable=False, index=True)
    firm_service_selection_id: Mapped[int | None] = mapped_column(ForeignKey("firm_service_selections.id", ondelete="SET NULL"), nullable=True, index=True)

    default_partner_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True, index=True)
    default_performing_partner_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True, index=True)
    default_manager_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True, index=True)
    default_staff_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True, index=True)
    default_review_partner_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True, index=True)

    recurrence_type: Mapped[str] = mapped_column(String(30), nullable=False, default="one_time", server_default="one_time", index=True)
    effective_from: Mapped[date | None] = mapped_column(Date, nullable=True)
    effective_to: Mapped[date | None] = mapped_column(Date, nullable=True)
    auto_generate_periods: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, server_default="false")
    status: Mapped[str] = mapped_column(String(30), nullable=False, default="active", server_default="active", index=True)
    is_active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True, server_default="true", index=True)
    remarks: Mapped[str | None] = mapped_column(Text, nullable=True)

    created_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True)
    updated_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), 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")
    catalogue = relationship("ServiceCatalogue")
    firm_selection = relationship("FirmServiceSelection")
    default_partner = relationship("User", foreign_keys=[default_partner_user_id])
    default_performing_partner = relationship("User", foreign_keys=[default_performing_partner_user_id])
    default_manager = relationship("User", foreign_keys=[default_manager_user_id])
    default_staff = relationship("User", foreign_keys=[default_staff_user_id])
    default_review_partner = relationship("User", foreign_keys=[default_review_partner_user_id])
    engagements = relationship("ClientServiceSubscription", back_populates="service_plan")


class ClientServiceSubscription(CommonBase):
    """Firm-level subscription of an enabled service to a specific client."""

    __tablename__ = "client_service_subscriptions"
    __table_args__ = (
        UniqueConstraint(
            "tenant_id",
            "service_catalogue_id",
            "scope_key",
            "financial_year",
            "period_label",
            name="uq_css_tenant_service_scope_fy_period",
        ),
    )

    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"), nullable=True, index=True)
    service_plan_id: Mapped[int | None] = mapped_column(ForeignKey("client_service_plans.id", ondelete="SET NULL"), nullable=True, index=True)
    client_id: Mapped[int] = mapped_column(ForeignKey("clients.id", ondelete="CASCADE"), nullable=False, index=True)
    scope_type: Mapped[str] = mapped_column(String(30), nullable=False, default="client", server_default="client", index=True)
    scope_key: Mapped[str] = mapped_column(String(80), nullable=False, default="", server_default="", index=True)
    business_unit_id: Mapped[int | None] = mapped_column(ForeignKey("client_business_units.id", ondelete="SET NULL"), nullable=True, index=True)
    client_branch_id: Mapped[int | None] = mapped_column(ForeignKey("client_branches.id", ondelete="SET NULL"), nullable=True, index=True)
    registration_id: Mapped[int | None] = mapped_column(ForeignKey("client_registrations.id", ondelete="SET NULL"), nullable=True, index=True)
    service_catalogue_id: Mapped[int] = mapped_column(ForeignKey("service_catalogues.id", ondelete="CASCADE"), nullable=False, index=True)
    firm_service_selection_id: Mapped[int | None] = mapped_column(ForeignKey("firm_service_selections.id", ondelete="SET NULL"), nullable=True, index=True)

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

    financial_year: Mapped[str] = mapped_column(String(9), nullable=False, default="2025-26", index=True)
    period_label: Mapped[str] = mapped_column(String(30), nullable=False, default="", server_default="", index=True)
    assessment_year: Mapped[str | None] = mapped_column(String(9), nullable=True, index=True)
    engagement_type: Mapped[str] = mapped_column(String(20), nullable=False, default="non_assurance", index=True)
    due_date_rule_id: Mapped[int | None] = mapped_column(ForeignKey("service_due_date_rules.id", ondelete="SET NULL"), nullable=True, index=True)
    original_due_date: Mapped[date | None] = mapped_column(Date, nullable=True, index=True)
    current_due_date: Mapped[date | None] = mapped_column(Date, nullable=True, index=True)
    due_date_source: Mapped[str | None] = mapped_column(String(30), nullable=True)
    expiry_date: Mapped[date | None] = mapped_column(Date, nullable=True, index=True)
    start_date: Mapped[date | None] = mapped_column(Date, nullable=True)
    end_date: Mapped[date | None] = mapped_column(Date, nullable=True)
    status: Mapped[str] = mapped_column(String(30), nullable=False, default="active", index=True)
    remarks: Mapped[str | None] = mapped_column(Text, nullable=True)

    # AQMM / engagement-level quality workflow. This is intentionally attached
    # to the engagement/subscription, not to the client master, because one
    # client can have both assurance and non-assurance services in the same FY.
    quality_workflow_required: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, index=True)
    quality_workflow_status: Mapped[str] = mapped_column(String(40), nullable=False, default="not_required", index=True)
    quality_acceptance_status: Mapped[str] = mapped_column(String(40), nullable=False, default="not_required", index=True)
    quality_independence_status: Mapped[str] = mapped_column(String(40), nullable=False, default="not_required", index=True)
    quality_conflict_status: Mapped[str] = mapped_column(String(40), nullable=False, default="not_required", index=True)
    quality_kyc_status: Mapped[str] = mapped_column(String(40), nullable=False, default="not_required", index=True)
    quality_engagement_letter_status: Mapped[str] = mapped_column(String(40), nullable=False, default="not_required", index=True)
    quality_approved_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True, index=True)
    quality_approved_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    quality_block_reason: Mapped[str | None] = mapped_column(Text, nullable=True)

    # Employee engagement workflow pause/dependency metadata (Phase 3).
    workflow_pause_reason: Mapped[str | None] = mapped_column(String(60), nullable=True, index=True)
    workflow_pause_notes: Mapped[str | None] = mapped_column(Text, nullable=True)
    workflow_follow_up_date: Mapped[date | None] = mapped_column(Date, nullable=True, index=True)
    workflow_paused_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    workflow_paused_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True, index=True)
    workflow_resumed_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    workflow_resumed_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True, index=True)

    is_active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True, index=True)
    is_locked: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, index=True)
    locked_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    locked_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True)

    created_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True)
    updated_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), 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)

    service_plan = relationship("ClientServicePlan", back_populates="engagements")
    client = relationship("Client")
    catalogue = relationship("ServiceCatalogue")
    due_date_rule = relationship("ServiceDueDateRule", foreign_keys=[due_date_rule_id])
    firm_selection = relationship("FirmServiceSelection")
    assigned_partner = relationship("User", foreign_keys=[assigned_partner_user_id])
    performing_partner = relationship("User", foreign_keys=[performing_partner_user_id])
    assigned_manager = relationship("User", foreign_keys=[assigned_manager_user_id])
    assigned_staff = relationship("User", foreign_keys=[assigned_staff_user_id])
    review_partner = relationship("User", foreign_keys=[review_partner_user_id])
    locked_by = relationship("User", foreign_keys=[locked_by_user_id])
    quality_approved_by = relationship("User", foreign_keys=[quality_approved_by_user_id])
    workflow_paused_by = relationship("User", foreign_keys=[workflow_paused_by_user_id])
    workflow_resumed_by = relationship("User", foreign_keys=[workflow_resumed_by_user_id])


class EngagementQualityDeclaration(CommonBase):
    """Digital AQMM declaration attached to an assurance engagement.

    Used for independence and conflict confirmations from the assigned partner,
    review partner, manager, staff and any other engagement team member.
    """

    __tablename__ = "engagement_quality_declarations"
    __table_args__ = (
        UniqueConstraint(
            "subscription_id",
            "declaration_type",
            "requested_user_id",
            name="uq_engagement_quality_declaration_user_type",
        ),
    )

    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)
    subscription_id: Mapped[int] = mapped_column(ForeignKey("client_service_subscriptions.id", ondelete="CASCADE"), nullable=False, index=True)

    declaration_type: Mapped[str] = mapped_column(String(40), nullable=False, index=True)  # independence / conflict
    requested_user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True)
    requested_role: Mapped[str | None] = mapped_column(String(80), nullable=True)
    status: Mapped[str] = mapped_column(String(40), nullable=False, default="pending", index=True)
    declaration_text: Mapped[str | None] = mapped_column(Text, nullable=True)
    response_notes: Mapped[str | None] = mapped_column(Text, nullable=True)
    responded_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    ip_address: Mapped[str | None] = mapped_column(String(80), nullable=True)
    user_agent: Mapped[str | None] = mapped_column(String(500), nullable=True)

    created_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)

    subscription = relationship("ClientServiceSubscription")
    client = relationship("Client")
    requested_user = relationship("User", foreign_keys=[requested_user_id])
    created_by = relationship("User", foreign_keys=[created_by_user_id])


class EngagementKycVerification(CommonBase):
    """KYC verification for an assurance engagement using existing permanent documents."""

    __tablename__ = "engagement_kyc_verifications"

    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)
    subscription_id: Mapped[int] = mapped_column(ForeignKey("client_service_subscriptions.id", ondelete="CASCADE"), nullable=False, index=True)

    status: Mapped[str] = mapped_column(String(40), nullable=False, default="pending_verification", index=True)
    source: Mapped[str] = mapped_column(String(80), nullable=False, default="permanent_documents")
    verification_notes: Mapped[str | None] = mapped_column(Text, nullable=True)
    verified_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True)
    verified_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)

    created_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)

    subscription = relationship("ClientServiceSubscription")
    client = relationship("Client")
    verified_by = relationship("User", foreign_keys=[verified_by_user_id])
    created_by = relationship("User", foreign_keys=[created_by_user_id])


class EngagementLetter(CommonBase):
    """Engagement-specific letter acceptance evidence.

    Supports both client OTP acceptance and manual signed PDF upload/verification.
    File upload storage can be extended without changing the AQMM gate fields.
    """

    __tablename__ = "engagement_letters"

    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)
    subscription_id: Mapped[int] = mapped_column(ForeignKey("client_service_subscriptions.id", ondelete="CASCADE"), nullable=False, index=True)

    title: Mapped[str] = mapped_column(String(255), nullable=False, default="Engagement Letter")
    version_no: Mapped[int] = mapped_column(Integer, nullable=False, default=1)
    status: Mapped[str] = mapped_column(String(50), nullable=False, default="draft_pending", index=True)
    acceptance_mode: Mapped[str | None] = mapped_column(String(40), nullable=True)  # digital_otp / manual_signed_upload
    body_text: Mapped[str | None] = mapped_column(Text, nullable=True)
    pdf_sha256: Mapped[str | None] = mapped_column(String(128), nullable=True)
    manual_upload_filename: Mapped[str | None] = mapped_column(String(255), nullable=True)
    manual_upload_path: Mapped[str | None] = mapped_column(String(500), nullable=True)
    client_acceptance_declaration: Mapped[str | None] = mapped_column(Text, nullable=True)

    partner_approved_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True)
    partner_approved_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    client_accepted_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True)
    client_accepted_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    manual_verified_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True)
    manual_verified_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    ip_address: Mapped[str | None] = mapped_column(String(80), nullable=True)
    user_agent: Mapped[str | None] = mapped_column(String(500), nullable=True)

    created_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)

    subscription = relationship("ClientServiceSubscription")
    client = relationship("Client")
    partner_approved_by = relationship("User", foreign_keys=[partner_approved_by_user_id])
    client_accepted_by = relationship("User", foreign_keys=[client_accepted_by_user_id])
    manual_verified_by = relationship("User", foreign_keys=[manual_verified_by_user_id])
    created_by = relationship("User", foreign_keys=[created_by_user_id])


class EngagementClosureChecklist(CommonBase):
    """Engagement closure checklist attached to the existing engagement.

    Closure is the final file-completion gate. It summarises evidence already
    available in AQMM acceptance, AQMM-tagged tasks, task documents/reviews,
    final released documents and UDIN controls. Only a few closure confirmations
    are manual; the rest are calculated from the existing workflow.
    """

    __tablename__ = "engagement_closure_checklists"
    __table_args__ = (
        UniqueConstraint("subscription_id", name="uq_engagement_closure_subscription"),
    )

    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)
    subscription_id: Mapped[int] = mapped_column(ForeignKey("client_service_subscriptions.id", ondelete="CASCADE"), nullable=False, index=True)

    closure_status: Mapped[str] = mapped_column(String(40), nullable=False, default="not_started", index=True)

    aqmm_acceptance_completed: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
    aqmm_tasks_completed: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
    evidence_review_completed: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
    final_documents_released: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
    udin_completed: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
    normal_tasks_completed: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)

    deliverables_sent_to_client: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
    billing_reviewed: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
    open_points_closed: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
    client_communication_completed: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)

    closure_note: Mapped[str | None] = mapped_column(Text, nullable=True)
    closure_block_reason: Mapped[str | None] = mapped_column(Text, nullable=True)

    closure_approved_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True)
    closure_approved_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)

    reopened_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True)
    reopened_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    reopen_reason: Mapped[str | None] = mapped_column(Text, nullable=True)

    created_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)

    subscription = relationship("ClientServiceSubscription")
    client = relationship("Client")
    closure_approved_by = relationship("User", foreign_keys=[closure_approved_by_user_id])
    reopened_by = relationship("User", foreign_keys=[reopened_by_user_id])
    created_by = relationship("User", foreign_keys=[created_by_user_id])


class ClientServiceTaskInstance(CommonBase):
    """Execution task generated from a firm service task template for a client-service subscription."""

    __tablename__ = "client_service_task_instances"
    __table_args__ = (
        UniqueConstraint(
            "subscription_id",
            "firm_task_template_id",
            "financial_year",
            name="uq_client_service_task_subscription_template_year",
        ),
    )

    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"), nullable=True, index=True)
    subscription_id: Mapped[int] = mapped_column(ForeignKey("client_service_subscriptions.id", ondelete="CASCADE"), nullable=False, index=True)
    client_id: Mapped[int] = mapped_column(ForeignKey("clients.id", ondelete="CASCADE"), nullable=False, index=True)
    service_catalogue_id: Mapped[int] = mapped_column(ForeignKey("service_catalogues.id", ondelete="CASCADE"), nullable=False, index=True)
    firm_task_template_id: Mapped[int | None] = mapped_column(ForeignKey("firm_service_task_templates.id", ondelete="SET NULL"), nullable=True, index=True)

    financial_year: Mapped[str] = mapped_column(String(9), nullable=False, default="2025-26", index=True)
    period_label: Mapped[str] = mapped_column(String(30), nullable=False, default="", server_default="", index=True)
    assessment_year: Mapped[str | None] = mapped_column(String(9), nullable=True, index=True)
    task_name: Mapped[str] = mapped_column(String(200), nullable=False)
    description: Mapped[str | None] = mapped_column(Text, nullable=True)
    sequence_no: Mapped[int] = mapped_column(Integer, nullable=False, default=1)
    default_role_name: Mapped[str | None] = mapped_column(String(100), nullable=True)
    eligible_role_names: Mapped[str | None] = mapped_column(String(200), nullable=True)

    assigned_to_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True, index=True)
    execution_mode: Mapped[str] = mapped_column(String(30), nullable=False, default="internal", index=True)
    assigned_consultant_id: Mapped[int | None] = mapped_column(ForeignKey("consultant_profiles.id", ondelete="SET NULL"), nullable=True, index=True)
    consultant_assignment_status: Mapped[str] = mapped_column(String(30), nullable=False, default="not_applicable", index=True)
    consultant_due_date: Mapped[date | None] = mapped_column(Date, nullable=True, index=True)
    consultant_offered_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    consultant_accepted_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    consultant_declined_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    consultant_decline_reason: Mapped[str | None] = mapped_column(Text, nullable=True)
    consultant_started_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    consultant_submitted_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    consultant_submission_note: Mapped[str | None] = mapped_column(Text, nullable=True)
    consultant_approved_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    consultant_approved_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True)
    internal_target_date: Mapped[date | None] = mapped_column(Date, nullable=True, index=True)
    status: Mapped[str] = mapped_column(String(30), nullable=False, default="pending", index=True)
    priority: Mapped[str] = mapped_column(String(20), nullable=False, default="normal")

    # Structured checklist snapshot fields. These columns already exist in the
    # production database via Alembic revision 20260719_task_checklist_fields.
    # Keeping them on the ORM model is required so task generation and the
    # existing Work Tracker checklist UI use the same persisted schema.
    task_category: Mapped[str | None] = mapped_column(String(100), nullable=True, index=True)
    response_required: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
    response_type: Mapped[str] = mapped_column(String(20), nullable=False, default="NONE")
    evidence_required: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
    remarks_required_if_no: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
    checklist_response: Mapped[str | None] = mapped_column(String(10), nullable=True)
    checklist_text_response: Mapped[str | None] = mapped_column(Text, nullable=True)
    checklist_number_response: Mapped[float | None] = mapped_column(Numeric(18, 2), nullable=True)
    checklist_date_response: Mapped[date | None] = mapped_column(Date, nullable=True)
    checklist_remarks: Mapped[str | None] = mapped_column(Text, nullable=True)

    # Copied from FirmServiceTaskTemplate at generation time. Existing task
    # completion/evidence upload flow is reused to calculate AQMM quality status.
    is_aqmm_task: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False, index=True)
    aqmm_mandatory: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    aqmm_evidence_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    aqmm_manager_review_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    aqmm_partner_review_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    aqmm_review_partner_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    aqmm_blocks_final_release: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    aqmm_reference: Mapped[str | None] = mapped_column(String(120), nullable=True)
    aqmm_status: Mapped[str] = mapped_column(String(40), default="not_required", nullable=False, index=True)
    aqmm_review_status: Mapped[str] = mapped_column(String(40), default="not_required", nullable=False, index=True)
    aqmm_completed_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)

    # Phase 4 - AQMM task evidence and review notes.
    # For AQMM-tagged tasks, documents, work notes and review notes on this
    # task become the AQMM evidence trail. Normal non-AQMM tasks continue to use
    # the same fields as optional operational notes.
    evidence_status: Mapped[str] = mapped_column(String(30), nullable=False, default="not_required", index=True)
    submitted_for_review_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True, index=True)
    submitted_for_review_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)

    manager_review_status: Mapped[str] = mapped_column(String(30), nullable=False, default="not_required", index=True)
    manager_review_note: Mapped[str | None] = mapped_column(Text, nullable=True)
    manager_reviewed_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True, index=True)
    manager_reviewed_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)

    partner_review_status: Mapped[str] = mapped_column(String(30), nullable=False, default="not_required", index=True)
    partner_review_note: Mapped[str | None] = mapped_column(Text, nullable=True)
    partner_reviewed_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True, index=True)
    partner_reviewed_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)

    review_partner_review_status: Mapped[str] = mapped_column(String(30), nullable=False, default="not_required", index=True)
    review_partner_review_note: Mapped[str | None] = mapped_column(Text, nullable=True)
    review_partner_reviewed_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True, index=True)
    review_partner_reviewed_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)

    rework_status: Mapped[str] = mapped_column(String(30), nullable=False, default="none", index=True)
    rework_reason: Mapped[str | None] = mapped_column(Text, nullable=True)
    rework_requested_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True, index=True)
    rework_requested_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    rework_resolved_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)

    remarks: Mapped[str | None] = mapped_column(Text, nullable=True)

    started_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    completed_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    is_active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True, index=True)
    is_locked: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, index=True)
    locked_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    locked_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True)

    created_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True)
    updated_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), 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)

    subscription = relationship("ClientServiceSubscription")
    client = relationship("Client")
    catalogue = relationship("ServiceCatalogue")
    template = relationship("FirmServiceTaskTemplate")
    assigned_to = relationship("User", foreign_keys=[assigned_to_user_id])
    assigned_consultant = relationship("ConsultantProfile", foreign_keys=[assigned_consultant_id])
    consultant_approved_by = relationship("User", foreign_keys=[consultant_approved_by_user_id])
    locked_by = relationship("User", foreign_keys=[locked_by_user_id])
    documents = relationship(
        "EngagementDocument",
        primaryjoin="ClientServiceTaskInstance.id == foreign(EngagementDocument.task_instance_id)",
        viewonly=True,
        order_by="EngagementDocument.updated_at_utc.desc()",
    )
    document_requests = relationship(
        "ServiceTaskDocumentRequest",
        back_populates="task",
        cascade="all, delete-orphan",
        passive_deletes=True,
        order_by="ServiceTaskDocumentRequest.created_at_utc.desc()",
    )
    comments = relationship(
        "ServiceTaskComment",
        back_populates="task",
        cascade="all, delete-orphan",
        passive_deletes=True,
        order_by="ServiceTaskComment.created_at_utc.desc()",
    )


class ServiceTaskDocumentRequest(CommonBase):
    """Live document/clarification requirement raised against an engagement task."""

    __tablename__ = "service_task_document_requests"

    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"), nullable=True, index=True)
    subscription_id: Mapped[int] = mapped_column(ForeignKey("client_service_subscriptions.id", ondelete="CASCADE"), nullable=False, index=True)
    task_instance_id: Mapped[int] = mapped_column(ForeignKey("client_service_task_instances.id", ondelete="CASCADE"), nullable=False, index=True)
    client_id: Mapped[int] = mapped_column(ForeignKey("clients.id", ondelete="CASCADE"), nullable=False, index=True)

    request_type: Mapped[str] = mapped_column(String(30), nullable=False, default="document", index=True)
    title: Mapped[str] = mapped_column(String(200), nullable=False)
    description: Mapped[str | None] = mapped_column(Text, nullable=True)
    requested_from: Mapped[str] = mapped_column(String(30), nullable=False, default="client_and_consultant", index=True)
    due_date: Mapped[date | None] = mapped_column(Date, nullable=True, index=True)
    status: Mapped[str] = mapped_column(String(30), nullable=False, default="pending", index=True)
    response_note: Mapped[str | None] = mapped_column(Text, nullable=True)
    received_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    verified_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)

    requested_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
    responded_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
    verified_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)
    is_active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True, index=True)

    task = relationship("ClientServiceTaskInstance", back_populates="document_requests")
    subscription = relationship("ClientServiceSubscription")
    client = relationship("Client")
    requested_by = relationship("User", foreign_keys=[requested_by_user_id])
    responded_by = relationship("User", foreign_keys=[responded_by_user_id])
    verified_by = relationship("User", foreign_keys=[verified_by_user_id])


class ServiceTaskComment(CommonBase):
    """Communication timeline entry linked to a service task instance."""

    __tablename__ = "service_task_comments"

    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"), nullable=True, index=True)
    subscription_id: Mapped[int] = mapped_column(ForeignKey("client_service_subscriptions.id", ondelete="CASCADE"), nullable=False, index=True)
    task_instance_id: Mapped[int] = mapped_column(ForeignKey("client_service_task_instances.id", ondelete="CASCADE"), nullable=False, index=True)

    comment_type: Mapped[str] = mapped_column(String(40), nullable=False, default="internal_note", index=True)
    visibility: Mapped[str] = mapped_column(String(30), nullable=False, default="internal", index=True)
    message: Mapped[str] = mapped_column(Text, nullable=False)

    created_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), 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)
    is_deleted: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, index=True)

    task = relationship("ClientServiceTaskInstance", back_populates="comments")
    subscription = relationship("ClientServiceSubscription")
    created_by = relationship("User", foreign_keys=[created_by_user_id])
