"""CRM domain aggregates — Phase 6.0 + 6.1 core entities.

Independent aggregates use UUID references within crm_db only.
No SQLAlchemy relationship graphs between aggregates.
"""
from __future__ import annotations

import uuid
from datetime import date, datetime
from decimal import Decimal

from sqlalchemy import (
    Boolean,
    Date,
    DateTime,
    Index,
    Integer,
    Numeric,
    String,
    Text,
    UniqueConstraint,
)
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy.types import JSON

from app.core.database import Base
from app.models.base import (
    ActorAuditMixin,
    SoftDeleteMixin,
    TenantMixin,
    TimestampMixin,
    UUIDPrimaryKeyMixin,
)
from app.models.types import (
    ActivityPriority,
    ActivityRelatedType,
    ActivityStatus,
    ActivityType,
    ContactKind,
    ContactMethod,
    ContactStatus,
    GUID,
    LeadPriority,
    LeadStatus,
    OpportunityStatus,
    OrganizationKind,
    OrganizationStatus,
    QuoteStatus,
)


class Lead(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    """Prospective customer / sales lead aggregate (Phase 6.1)."""

    __tablename__ = "leads"

    lead_number: Mapped[str] = mapped_column(String(50), nullable=False)
    first_name: Mapped[str] = mapped_column(String(100), nullable=False, default="")
    last_name: Mapped[str] = mapped_column(String(100), nullable=False, default="")
    full_name: Mapped[str] = mapped_column(String(255), nullable=False)
    title: Mapped[str | None] = mapped_column(String(255), nullable=True)
    company: Mapped[str | None] = mapped_column(String(255), nullable=True)
    job_title: Mapped[str | None] = mapped_column(String(150), nullable=True)
    email: Mapped[str | None] = mapped_column(String(255), nullable=True)
    mobile: Mapped[str | None] = mapped_column(String(50), nullable=True)
    phone: Mapped[str | None] = mapped_column(String(50), nullable=True)
    website: Mapped[str | None] = mapped_column(String(255), nullable=True)
    lead_source_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    lead_status_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    status: Mapped[LeadStatus] = mapped_column(default=LeadStatus.NEW, nullable=False)
    source: Mapped[str | None] = mapped_column(String(100), nullable=True)
    assigned_user_id: Mapped[str | None] = mapped_column(String(100), nullable=True)
    priority: Mapped[LeadPriority] = mapped_column(
        default=LeadPriority.MEDIUM, nullable=False
    )
    estimated_value: Mapped[Decimal] = mapped_column(
        Numeric(18, 4), default=Decimal("0"), nullable=False
    )
    expected_close_date: Mapped[date | None] = mapped_column(Date, nullable=True)
    description: Mapped[str | None] = mapped_column(Text, nullable=True)
    owner_user_id: Mapped[str] = mapped_column(String(100), nullable=False)
    contact_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    organization_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    # legacy alias kept for compatibility with early 6.0 payloads
    company_name: Mapped[str | None] = mapped_column(String(255), nullable=True)

    __table_args__ = (
        UniqueConstraint("tenant_id", "lead_number", name="uq_leads_tenant_lead_number"),
        Index("ix_leads_tenant_status", "tenant_id", "status"),
        Index("ix_leads_tenant_email", "tenant_id", "email"),
        Index("ix_leads_tenant_owner", "tenant_id", "owner_user_id"),
        Index("ix_leads_tenant_deleted", "tenant_id", "is_deleted"),
    )


class Contact(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    """Person contact aggregate (Phase 6.1)."""

    __tablename__ = "contacts"

    first_name: Mapped[str] = mapped_column(String(100), nullable=False)
    last_name: Mapped[str] = mapped_column(String(100), nullable=False)
    full_name: Mapped[str] = mapped_column(String(255), nullable=False)
    kind: Mapped[ContactKind] = mapped_column(
        default=ContactKind.BUSINESS, nullable=False
    )
    contact_type_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    email: Mapped[str | None] = mapped_column(String(255), nullable=True)
    phone: Mapped[str | None] = mapped_column(String(50), nullable=True)
    emails: Mapped[list | None] = mapped_column(JSON, nullable=True)
    phones: Mapped[list | None] = mapped_column(JSON, nullable=True)
    default_contact_method: Mapped[ContactMethod | None] = mapped_column(nullable=True)
    social_links: Mapped[dict | None] = mapped_column(JSON, nullable=True)
    birthday: Mapped[date | None] = mapped_column(Date, nullable=True)
    job_title: Mapped[str | None] = mapped_column(String(150), nullable=True)
    job_position: Mapped[str | None] = mapped_column(String(150), nullable=True)
    status: Mapped[ContactStatus] = mapped_column(
        default=ContactStatus.ACTIVE, nullable=False
    )
    organization_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    owner_user_id: Mapped[str | None] = mapped_column(String(100), nullable=True)
    notes_summary: Mapped[str | None] = mapped_column(Text, nullable=True)

    __table_args__ = (
        Index("ix_contacts_tenant_email", "tenant_id", "email"),
        Index("ix_contacts_tenant_org", "tenant_id", "organization_id"),
        Index("ix_contacts_tenant_deleted", "tenant_id", "is_deleted"),
    )


class Organization(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    """Business account / organization aggregate (Phase 6.1)."""

    __tablename__ = "organizations"

    name: Mapped[str] = mapped_column(String(255), nullable=False)
    kind: Mapped[OrganizationKind] = mapped_column(
        default=OrganizationKind.COMPANY, nullable=False
    )
    legal_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
    trade_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
    national_identifier: Mapped[str | None] = mapped_column(String(50), nullable=True)
    tax_number: Mapped[str | None] = mapped_column(String(50), nullable=True)
    organization_type_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    organization_industry_id: Mapped[uuid.UUID | None] = mapped_column(
        GUID(), nullable=True
    )
    industry: Mapped[str | None] = mapped_column(String(150), nullable=True)
    website: Mapped[str | None] = mapped_column(String(255), nullable=True)
    email: Mapped[str | None] = mapped_column(String(255), nullable=True)
    phone: Mapped[str | None] = mapped_column(String(50), nullable=True)
    parent_organization_id: Mapped[uuid.UUID | None] = mapped_column(
        GUID(), nullable=True
    )
    status: Mapped[OrganizationStatus] = mapped_column(
        default=OrganizationStatus.ACTIVE, nullable=False
    )
    owner_user_id: Mapped[str | None] = mapped_column(String(100), nullable=True)
    description: Mapped[str | None] = mapped_column(Text, nullable=True)

    __table_args__ = (
        UniqueConstraint("tenant_id", "name", name="uq_organizations_tenant_name"),
        Index("ix_organizations_tenant_status", "tenant_id", "status"),
        Index("ix_organizations_tenant_parent", "tenant_id", "parent_organization_id"),
        Index("ix_organizations_tenant_deleted", "tenant_id", "is_deleted"),
    )


class Pipeline(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    """Sales pipeline aggregate (Phase 6.2)."""

    __tablename__ = "pipelines"

    name: Mapped[str] = mapped_column(String(255), nullable=False)
    code: Mapped[str] = mapped_column(String(50), nullable=False)
    description: Mapped[str | None] = mapped_column(Text, nullable=True)
    sort_order: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    is_default: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
    status: Mapped[str] = mapped_column(String(20), default="active", nullable=False)

    __table_args__ = (
        UniqueConstraint("tenant_id", "code", name="uq_pipelines_tenant_code"),
    )


class PipelineStage(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    """Pipeline stage aggregate (Phase 6.2)."""

    __tablename__ = "pipeline_stages"

    pipeline_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    name: Mapped[str] = mapped_column(String(255), nullable=False)
    code: Mapped[str] = mapped_column(String(50), nullable=False)
    position: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    win_probability: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    color: Mapped[str | None] = mapped_column(String(20), nullable=True)
    is_start_stage: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    is_won_stage: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    is_lost_stage: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    is_closed_stage: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    required_fields: Mapped[list | None] = mapped_column(JSON, nullable=True)
    exit_validation: Mapped[dict | None] = mapped_column(JSON, nullable=True)
    is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id", "pipeline_id", "code", name="uq_pipeline_stages_tenant_code"
        ),
        Index("ix_pipeline_stages_pipeline", "tenant_id", "pipeline_id"),
    )


class Opportunity(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    """Sales opportunity aggregate (Phase 6.2)."""

    __tablename__ = "opportunities"

    opportunity_number: Mapped[str] = mapped_column(String(50), nullable=False)
    name: Mapped[str] = mapped_column(String(255), nullable=False)
    title: Mapped[str] = mapped_column(String(255), nullable=False)
    pipeline_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    stage_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    status: Mapped[OpportunityStatus] = mapped_column(
        default=OpportunityStatus.OPEN, nullable=False
    )
    amount: Mapped[Decimal] = mapped_column(
        Numeric(18, 4), default=Decimal("0"), nullable=False
    )
    expected_revenue: Mapped[Decimal] = mapped_column(
        Numeric(18, 4), default=Decimal("0"), nullable=False
    )
    currency_code: Mapped[str] = mapped_column(String(3), default="IRR", nullable=False)
    probability: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    priority: Mapped[str] = mapped_column(String(20), default="medium", nullable=False)
    expected_close_date: Mapped[date | None] = mapped_column(Date, nullable=True)
    organization_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    contact_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    lead_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    owner_user_id: Mapped[str | None] = mapped_column(String(100), nullable=True)
    description: Mapped[str | None] = mapped_column(Text, nullable=True)
    won_at: Mapped[datetime | None] = mapped_column(
        DateTime(timezone=True), nullable=True
    )
    lost_at: Mapped[datetime | None] = mapped_column(
        DateTime(timezone=True), nullable=True
    )

    __table_args__ = (
        UniqueConstraint(
            "tenant_id",
            "opportunity_number",
            name="uq_opportunities_tenant_number",
        ),
        Index("ix_opportunities_tenant_status", "tenant_id", "status"),
        Index(
            "ix_opportunities_pipeline_stage", "tenant_id", "pipeline_id", "stage_id"
        ),
        Index("ix_opportunities_tenant_deleted", "tenant_id", "is_deleted"),
    )


class SalesActivity(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    """Sales activity aggregate (Phase 6.3 collaboration)."""

    __tablename__ = "sales_activities"

    subject: Mapped[str] = mapped_column(String(255), nullable=False)
    activity_type: Mapped[ActivityType] = mapped_column(nullable=False)
    status: Mapped[ActivityStatus] = mapped_column(
        default=ActivityStatus.PLANNED, nullable=False
    )
    priority: Mapped[ActivityPriority] = mapped_column(
        default=ActivityPriority.MEDIUM, nullable=False
    )
    related_type: Mapped[ActivityRelatedType | None] = mapped_column(nullable=True)
    related_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    lead_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    contact_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    organization_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    opportunity_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    due_at: Mapped[datetime | None] = mapped_column(
        DateTime(timezone=True), nullable=True
    )
    completed_at: Mapped[datetime | None] = mapped_column(
        DateTime(timezone=True), nullable=True
    )
    owner_user_id: Mapped[str | None] = mapped_column(String(100), nullable=True)
    description: Mapped[str | None] = mapped_column(Text, nullable=True)
    custom_type_label: Mapped[str | None] = mapped_column(String(100), nullable=True)

    __table_args__ = (
        Index("ix_sales_activities_tenant_status", "tenant_id", "status"),
        Index(
            "ix_sales_activities_related", "tenant_id", "related_type", "related_id"
        ),
        Index("ix_sales_activities_opportunity", "tenant_id", "opportunity_id"),
    )


class Quote(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    """Sales quote aggregate (CRM-owned; not accounting invoice)."""

    __tablename__ = "quotes"

    title: Mapped[str] = mapped_column(String(255), nullable=False)
    quote_number: Mapped[str] = mapped_column(String(50), nullable=False)
    status: Mapped[QuoteStatus] = mapped_column(
        default=QuoteStatus.DRAFT, nullable=False
    )
    amount: Mapped[Decimal] = mapped_column(
        Numeric(18, 4), default=Decimal("0"), nullable=False
    )
    currency_code: Mapped[str] = mapped_column(String(3), default="IRR", nullable=False)
    valid_until: Mapped[date | None] = mapped_column(Date, nullable=True)
    opportunity_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    organization_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    contact_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    owner_user_id: Mapped[str | None] = mapped_column(String(100), nullable=True)
    notes: Mapped[str | None] = mapped_column(Text, nullable=True)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id", "quote_number", name="uq_quotes_tenant_quote_number"
        ),
        Index("ix_quotes_tenant_status", "tenant_id", "status"),
    )
