"""Phase 6.2 — Enterprise Sales Process Engine models."""
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 (
    ContactRoleType,
    ForecastKind,
    ForecastPeriod,
    GoalMetric,
    GoalScope,
    GUID,
    PlaybookAssignmentStatus,
    PlaybookStepKind,
    TargetPeriod,
    WinLossOutcome,
)


class SalesProcess(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    """Named sales process definition optionally bound to a pipeline."""

    __tablename__ = "sales_processes"

    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)
    pipeline_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)

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


class SalesStageHistory(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "sales_stage_history"

    opportunity_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    previous_stage_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    next_stage_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    changed_by: Mapped[str | None] = mapped_column(String(100), nullable=True)
    changed_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
    duration_seconds: Mapped[int | None] = mapped_column(Integer, nullable=True)
    reason: Mapped[str | None] = mapped_column(Text, nullable=True)

    __table_args__ = (
        Index("ix_sales_stage_history_opp", "tenant_id", "opportunity_id"),
    )


class OpportunityProduct(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    """Product line on opportunity — references Product Service only (no inventory)."""

    __tablename__ = "opportunity_products"

    opportunity_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    product_ref_id: Mapped[str] = mapped_column(String(100), nullable=False)
    product_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
    quantity: Mapped[Decimal] = mapped_column(
        Numeric(18, 4), default=Decimal("1"), nullable=False
    )
    unit_price: Mapped[Decimal] = mapped_column(
        Numeric(18, 4), default=Decimal("0"), nullable=False
    )
    discount_percent: Mapped[Decimal] = mapped_column(
        Numeric(8, 4), default=Decimal("0"), nullable=False
    )
    expected_revenue: Mapped[Decimal] = mapped_column(
        Numeric(18, 4), default=Decimal("0"), nullable=False
    )

    __table_args__ = (
        Index("ix_opportunity_products_opp", "tenant_id", "opportunity_id"),
    )


class OpportunityContact(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "opportunity_contacts"

    opportunity_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    contact_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    role: Mapped[ContactRoleType] = mapped_column(
        default=ContactRoleType.INFLUENCER, nullable=False
    )
    custom_role_label: Mapped[str | None] = mapped_column(String(100), nullable=True)
    is_primary: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id",
            "opportunity_id",
            "contact_id",
            "role",
            name="uq_opportunity_contacts_role",
        ),
        Index("ix_opportunity_contacts_opp", "tenant_id", "opportunity_id"),
    )


class OpportunityCompetitor(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "opportunity_competitors"

    opportunity_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    competitor_name: Mapped[str] = mapped_column(String(255), nullable=False)
    strength: Mapped[str | None] = mapped_column(Text, nullable=True)
    weakness: Mapped[str | None] = mapped_column(Text, nullable=True)
    notes: Mapped[str | None] = mapped_column(Text, nullable=True)
    win_probability: Mapped[int] = mapped_column(Integer, default=0, nullable=False)

    __table_args__ = (
        Index("ix_opportunity_competitors_opp", "tenant_id", "opportunity_id"),
    )


class SalesPlaybook(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    __tablename__ = "sales_playbooks"

    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)
    version: Mapped[int] = mapped_column(Integer, default=1, nullable=False)
    pipeline_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id", "code", "version", name="uq_sales_playbooks_code_version"
        ),
    )


class SalesPlaybookStep(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "sales_playbook_steps"

    playbook_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    title: Mapped[str] = mapped_column(String(255), nullable=False)
    step_kind: Mapped[PlaybookStepKind] = mapped_column(nullable=False)
    position: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    is_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    checklist_items: Mapped[list | None] = mapped_column(JSON, nullable=True)
    template_body: Mapped[str | None] = mapped_column(Text, nullable=True)

    __table_args__ = (
        Index("ix_sales_playbook_steps_playbook", "tenant_id", "playbook_id"),
    )


class SalesPlaybookAssignment(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "sales_playbook_assignments"

    playbook_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    opportunity_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    pipeline_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    status: Mapped[PlaybookAssignmentStatus] = mapped_column(
        default=PlaybookAssignmentStatus.ASSIGNED, nullable=False
    )
    assigned_by: Mapped[str | None] = mapped_column(String(100), nullable=True)
    completed_steps: Mapped[list | None] = mapped_column(JSON, nullable=True)
    completion_percent: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    completed_at: Mapped[datetime | None] = mapped_column(
        DateTime(timezone=True), nullable=True
    )

    __table_args__ = (
        Index("ix_sales_playbook_assignments_opp", "tenant_id", "opportunity_id"),
        Index("ix_sales_playbook_assignments_pb", "tenant_id", "playbook_id"),
    )


class WinLossCategory(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "win_loss_categories"

    code: Mapped[str] = mapped_column(String(50), nullable=False)
    name: Mapped[str] = mapped_column(String(150), nullable=False)
    parent_category_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)

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


class WinLossReason(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "win_loss_reasons"

    code: Mapped[str] = mapped_column(String(50), nullable=False)
    name: Mapped[str] = mapped_column(String(150), nullable=False)
    outcome: Mapped[WinLossOutcome] = mapped_column(nullable=False)
    category_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)

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


class OpportunityWinLoss(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    """Immutable win/loss analysis snapshot after opportunity close."""

    __tablename__ = "opportunity_win_loss"

    opportunity_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    outcome: Mapped[WinLossOutcome] = mapped_column(nullable=False)
    reason_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    category_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    sub_category: Mapped[str | None] = mapped_column(String(150), nullable=True)
    root_cause: Mapped[str | None] = mapped_column(Text, nullable=True)
    competitor_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
    price_factor: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    feature_gap: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    timing_factor: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    relationship_factor: Mapped[bool] = mapped_column(
        Boolean, default=False, nullable=False
    )
    approval_factor: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    custom_reason: Mapped[str | None] = mapped_column(Text, nullable=True)
    recorded_by: Mapped[str | None] = mapped_column(String(100), nullable=True)
    recorded_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), nullable=False
    )
    is_locked: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id", "opportunity_id", name="uq_opportunity_win_loss_opp"
        ),
    )


class SalesForecast(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "sales_forecasts"

    name: Mapped[str] = mapped_column(String(255), nullable=False)
    kind: Mapped[ForecastKind] = mapped_column(nullable=False)
    period: Mapped[ForecastPeriod] = mapped_column(nullable=False)
    period_start: Mapped[date] = mapped_column(Date, nullable=False)
    period_end: Mapped[date] = mapped_column(Date, nullable=False)
    pipeline_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    owner_user_id: Mapped[str | None] = mapped_column(String(100), nullable=True)
    pipeline_amount: Mapped[Decimal] = mapped_column(
        Numeric(18, 4), default=Decimal("0"), nullable=False
    )
    weighted_amount: Mapped[Decimal] = mapped_column(
        Numeric(18, 4), default=Decimal("0"), nullable=False
    )
    committed_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)
    opportunity_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False)

    __table_args__ = (
        Index("ix_sales_forecasts_period", "tenant_id", "period_start", "period_end"),
    )


class SalesGoal(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "sales_goals"

    name: Mapped[str] = mapped_column(String(255), nullable=False)
    scope: Mapped[GoalScope] = mapped_column(nullable=False)
    metric: Mapped[GoalMetric] = mapped_column(nullable=False)
    owner_user_id: Mapped[str | None] = mapped_column(String(100), nullable=True)
    team_key: Mapped[str | None] = mapped_column(String(100), nullable=True)
    department_key: Mapped[str | None] = mapped_column(String(100), nullable=True)
    target_value: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
    period_start: Mapped[date] = mapped_column(Date, nullable=False)
    period_end: Mapped[date] = mapped_column(Date, nullable=False)
    is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)

    __table_args__ = (
        Index("ix_sales_goals_owner", "tenant_id", "owner_user_id"),
    )


class SalesTarget(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "sales_targets"

    name: Mapped[str] = mapped_column(String(255), nullable=False)
    period: Mapped[TargetPeriod] = mapped_column(nullable=False)
    period_start: Mapped[date] = mapped_column(Date, nullable=False)
    period_end: Mapped[date] = mapped_column(Date, nullable=False)
    goal_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    owner_user_id: Mapped[str | None] = mapped_column(String(100), nullable=True)
    target_value: Mapped[Decimal] = mapped_column(Numeric(18, 4), nullable=False)
    actual_value: Mapped[Decimal] = mapped_column(
        Numeric(18, 4), default=Decimal("0"), nullable=False
    )
    achievement_percent: Mapped[Decimal] = mapped_column(
        Numeric(8, 4), default=Decimal("0"), nullable=False
    )

    __table_args__ = (
        Index("ix_sales_targets_period", "tenant_id", "period_start", "period_end"),
    )


class SalesAudit(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    """Sales-engine audit trail (distinct from generic crm_audit)."""

    __tablename__ = "sales_audit"

    entity_type: Mapped[str] = mapped_column(String(50), nullable=False)
    entity_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    action: Mapped[str] = mapped_column(String(50), nullable=False)
    actor_user_id: Mapped[str | None] = mapped_column(String(100), nullable=True)
    changes: Mapped[dict | None] = mapped_column(JSON, nullable=True)
    message: Mapped[str | None] = mapped_column(Text, nullable=True)

    __table_args__ = (
        Index("ix_sales_audit_entity", "tenant_id", "entity_type", "entity_id"),
    )
