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 ConsultantProfile(CommonBase):
    """Portal-enabled consultant / ecosystem partner profile.

    The login/security account remains in users. This table stores consultant
    business/profile details and links the consultant user to firm clients.
    """

    __tablename__ = "consultant_profiles"
    __table_args__ = (
        UniqueConstraint("tenant_id", "user_id", name="uq_consultant_profiles_tenant_user"),
        UniqueConstraint("tenant_id", "email", name="uq_consultant_profiles_tenant_email"),
    )

    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)
    user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True)

    consultant_type: Mapped[str] = mapped_column(String(50), nullable=False, default="external_consultant", index=True)
    firm_name: Mapped[str | None] = mapped_column(String(200), nullable=True)
    contact_person: Mapped[str] = mapped_column(String(200), nullable=False)
    email: Mapped[str | None] = mapped_column(String(255), nullable=True, index=True)
    mobile: Mapped[str | None] = mapped_column(String(20), nullable=True)
    specialisation: Mapped[str | None] = mapped_column(String(200), nullable=True)
    gstin: Mapped[str | None] = mapped_column(String(20), nullable=True)
    pan: Mapped[str | None] = mapped_column(String(20), nullable=True)
    address: Mapped[str | None] = mapped_column(Text, nullable=True)
    status: Mapped[str] = mapped_column(String(30), nullable=False, default="active", index=True)
    onboarding_status: Mapped[str] = mapped_column(String(30), nullable=False, default="approved", index=True)

    is_platform_partner: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
    is_franchise_partner: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
    is_saas_customer: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
    is_active: Mapped[bool] = mapped_column(Boolean, nullable=False, 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,
    )

    user = relationship("User", foreign_keys=[user_id])
    links = relationship(
        "ClientConsultantLink",
        back_populates="consultant",
        cascade="all, delete-orphan",
        passive_deletes=True,
    )
    workspace = relationship(
        "ConsultantWorkspace",
        back_populates="consultant",
        uselist=False,
        cascade="all, delete-orphan",
        passive_deletes=True,
    )
    managed_clients = relationship(
        "ConsultantManagedClient",
        back_populates="consultant",
        cascade="all, delete-orphan",
        passive_deletes=True,
    )
    service_requests = relationship(
        "ConsultantServiceRequest",
        back_populates="consultant",
        cascade="all, delete-orphan",
        passive_deletes=True,
    )


class ConsultantWorkspace(CommonBase):
    """SaaS/franchise workspace settings for a consultant portal account.

    This is a foundation table only. It does not change firm-owned clients or
    consultant-managed clients. It records whether the consultant operates as a
    SaaS customer, franchise partner, platform partner, or a normal external
    consultant, along with soft limits used by later subscription/billing phases.
    """

    __tablename__ = "consultant_workspaces"
    __table_args__ = (
        UniqueConstraint("tenant_id", "consultant_id", name="uq_consultant_workspaces_tenant_consultant"),
        UniqueConstraint("tenant_id", "workspace_code", name="uq_consultant_workspaces_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"), nullable=True, index=True)
    consultant_id: Mapped[int] = mapped_column(
        ForeignKey("consultant_profiles.id", ondelete="CASCADE"), nullable=False, index=True
    )

    workspace_code: Mapped[str] = mapped_column(String(50), nullable=False, index=True)
    workspace_name: Mapped[str] = mapped_column(String(200), nullable=False)
    workspace_type: Mapped[str] = mapped_column(String(50), nullable=False, default="consultant_saas", index=True)
    plan_code: Mapped[str] = mapped_column(String(50), nullable=False, default="starter", index=True)
    billing_cycle: Mapped[str] = mapped_column(String(30), nullable=False, default="manual", index=True)
    subscription_status: Mapped[str] = mapped_column(String(30), nullable=False, default="trial", index=True)
    subscription_start_date: Mapped[date | None] = mapped_column(Date, nullable=True)
    subscription_end_date: Mapped[date | None] = mapped_column(Date, nullable=True)

    max_managed_clients: Mapped[int] = mapped_column(Integer, nullable=False, default=25)
    max_user_accounts: Mapped[int] = mapped_column(Integer, nullable=False, default=1)
    allow_client_portal: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
    allow_firm_referrals: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
    allow_service_marketplace: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)

    is_active: Mapped[bool] = mapped_column(Boolean, nullable=False, 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,
    )

    consultant = relationship("ConsultantProfile", back_populates="workspace")


class ClientConsultantLink(CommonBase):
    """Explicit link between a firm client and a consultant portal profile."""

    __tablename__ = "client_consultant_links"
    __table_args__ = (
        UniqueConstraint("tenant_id", "client_id", "consultant_id", name="uq_client_consultant_links_client_consultant"),
    )

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

    relationship_type: Mapped[str] = mapped_column(String(50), nullable=False, default="accounts_consultant", index=True)
    is_primary: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
    can_view_client: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
    can_view_services: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
    can_view_due_dates: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
    can_view_communications: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
    can_view_engagements: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
    can_view_task_status: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
    can_view_assignee: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
    can_view_document_requests: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
    can_upload_documents: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
    can_reply_to_clarifications: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
    can_view_filing_details: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
    can_view_final_documents: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
    can_view_permanent_documents: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
    can_receive_notifications: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
    can_act_for_client: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
    effective_from: Mapped[date | None] = mapped_column(Date, nullable=True)
    effective_to: Mapped[date | None] = mapped_column(Date, nullable=True)
    is_active: Mapped[bool] = mapped_column(Boolean, nullable=False, 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,
    )

    consultant = relationship("ConsultantProfile", back_populates="links")
    client = relationship("Client")
    service_catalogue = relationship("ServiceCatalogue")


class ConsultantManagedClient(CommonBase):
    """Client/contact managed by a consultant inside the consultant portal.

    This table is intentionally separate from the firm `clients` master. It lets
    consultants maintain their own client book without affecting audit-firm
    client records. A later phase can convert/link a managed client to the firm
    client master through an approval workflow.
    """

    __tablename__ = "consultant_managed_clients"
    __table_args__ = (
        UniqueConstraint("tenant_id", "consultant_id", "client_code", name="uq_consultant_managed_clients_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"), nullable=True, index=True)
    consultant_id: Mapped[int] = mapped_column(
        ForeignKey("consultant_profiles.id", ondelete="CASCADE"), nullable=False, index=True
    )
    linked_firm_client_id: Mapped[int | None] = mapped_column(ForeignKey("clients.id", ondelete="SET NULL"), nullable=True, index=True)

    client_code: Mapped[str | None] = mapped_column(String(50), nullable=True, index=True)
    client_name: Mapped[str] = mapped_column(String(200), nullable=False, index=True)
    trade_name: Mapped[str | None] = mapped_column(String(200), nullable=True)
    client_type: Mapped[str] = mapped_column(String(100), nullable=False, default="Other")
    pan: Mapped[str | None] = mapped_column(String(20), nullable=True, index=True)
    gstin: Mapped[str | None] = mapped_column(String(20), nullable=True, index=True)
    tan: Mapped[str | None] = mapped_column(String(20), nullable=True)

    contact_person_name: Mapped[str | None] = mapped_column(String(200), nullable=True)
    mobile: Mapped[str | None] = mapped_column(String(20), nullable=True)
    email: Mapped[str | None] = mapped_column(String(255), nullable=True, index=True)
    address_line_1: Mapped[str | None] = mapped_column(String(255), nullable=True)
    address_line_2: Mapped[str | None] = mapped_column(String(255), nullable=True)
    city: Mapped[str | None] = mapped_column(String(100), nullable=True)
    state: Mapped[str | None] = mapped_column(String(100), nullable=True)
    pincode: Mapped[str | None] = mapped_column(String(20), nullable=True)
    country: Mapped[str | None] = mapped_column(String(100), nullable=True, default="India")

    service_interest: Mapped[str | None] = mapped_column(Text, nullable=True)
    relationship_stage: Mapped[str] = mapped_column(String(30), nullable=False, default="managed", index=True)
    status: Mapped[str] = mapped_column(String(30), nullable=False, default="active", index=True)
    is_active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True, index=True)
    notes: Mapped[str | None] = mapped_column(Text, nullable=True)

    conversion_status: Mapped[str] = mapped_column(String(30), nullable=False, default="not_requested", index=True)
    conversion_requested_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    conversion_requested_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True, index=True)
    conversion_reviewed_at_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    conversion_reviewed_by_user_id: Mapped[int | None] = mapped_column(ForeignKey("users.id"), nullable=True, index=True)
    conversion_notes: Mapped[str | None] = mapped_column(Text, nullable=True)
    conversion_firm_notes: 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,
    )

    consultant = relationship("ConsultantProfile", back_populates="managed_clients")
    linked_firm_client = relationship("Client")


class ConsultantServiceRequest(CommonBase):
    """Service request raised by a consultant to the audit firm.

    A request can relate either to a consultant-managed client or to an already
    linked firm client. The request itself does not create engagements; firm
    users review it first and decide whether to accept, reject, or keep it under
    review.
    """

    __tablename__ = "consultant_service_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)
    consultant_id: Mapped[int] = mapped_column(
        ForeignKey("consultant_profiles.id", ondelete="CASCADE"), nullable=False, index=True
    )
    managed_client_id: Mapped[int | None] = mapped_column(
        ForeignKey("consultant_managed_clients.id", ondelete="SET NULL"), nullable=True, index=True
    )
    firm_client_id: Mapped[int | None] = mapped_column(ForeignKey("clients.id", ondelete="SET NULL"), nullable=True, index=True)
    service_catalogue_id: Mapped[int | None] = mapped_column(
        ForeignKey("service_catalogues.id", ondelete="SET NULL"), nullable=True, index=True
    )

    request_no: Mapped[str] = mapped_column(String(50), nullable=False, index=True)
    request_type: Mapped[str] = mapped_column(String(50), nullable=False, default="service_request", index=True)
    status: Mapped[str] = mapped_column(String(40), nullable=False, default="submitted", index=True)
    priority: Mapped[str] = mapped_column(String(30), nullable=False, default="normal", index=True)
    requested_service_name: Mapped[str] = mapped_column(String(200), nullable=False)
    requested_due_date: Mapped[date | None] = mapped_column(Date, nullable=True)
    subject: Mapped[str] = mapped_column(String(255), nullable=False)
    description: Mapped[str | None] = mapped_column(Text, nullable=True)
    consultant_notes: Mapped[str | None] = mapped_column(Text, nullable=True)
    firm_response: Mapped[str | None] = mapped_column(Text, nullable=True)

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

    consultant = relationship("ConsultantProfile", back_populates="service_requests")
    managed_client = relationship("ConsultantManagedClient")
    firm_client = relationship("Client")
    service_catalogue = relationship("ServiceCatalogue")
    created_by = relationship("User", foreign_keys=[created_by_user_id])
    reviewed_by = relationship("User", foreign_keys=[reviewed_by_user_id])
