"""Phase 6.1 supporting CRM entities — lookups, tags, addresses, notes, attachments, audit."""
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 (
    AddressType,
    AttachmentKind,
    AuditAction,
    CrmEntityType,
    CustomFieldType,
    GUID,
    TagScope,
)


class LeadSource(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "lead_sources"

    code: Mapped[str] = mapped_column(String(50), nullable=False)
    name: Mapped[str] = mapped_column(String(150), nullable=False)
    description: Mapped[str | None] = mapped_column(Text, nullable=True)
    is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)

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


class LeadStatusDefinition(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "lead_statuses"

    code: Mapped[str] = mapped_column(String(50), nullable=False)
    name: Mapped[str] = mapped_column(String(150), nullable=False)
    position: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    is_terminal: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    is_converted: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
    color: Mapped[str | None] = mapped_column(String(20), nullable=True)

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


class ContactType(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "contact_types"

    code: Mapped[str] = mapped_column(String(50), nullable=False)
    name: Mapped[str] = mapped_column(String(150), nullable=False)
    is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)

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


class OrganizationType(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "organization_types"

    code: Mapped[str] = mapped_column(String(50), nullable=False)
    name: Mapped[str] = mapped_column(String(150), nullable=False)
    is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)

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


class OrganizationIndustry(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "organization_industries"

    code: Mapped[str] = mapped_column(String(50), nullable=False)
    name: Mapped[str] = mapped_column(String(150), nullable=False)
    is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)

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


class LeadAssignment(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "lead_assignments"

    lead_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    assigned_user_id: Mapped[str] = mapped_column(String(100), nullable=False)
    assigned_by: Mapped[str | None] = mapped_column(String(100), nullable=True)
    assigned_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), nullable=False
    )
    note: Mapped[str | None] = mapped_column(Text, nullable=True)
    is_current: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)

    __table_args__ = (
        Index("ix_lead_assignments_lead", "tenant_id", "lead_id"),
        Index("ix_lead_assignments_user", "tenant_id", "assigned_user_id"),
    )


class CrmTag(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "crm_tags"

    name: Mapped[str] = mapped_column(String(100), nullable=False)
    color: Mapped[str | None] = mapped_column(String(20), nullable=True)
    group_name: Mapped[str | None] = mapped_column(String(100), nullable=True)
    scope: Mapped[TagScope] = mapped_column(default=TagScope.ANY, nullable=False)
    is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)

    __table_args__ = (
        UniqueConstraint("tenant_id", "name", name="uq_crm_tags_tenant_name"),
    )


class LeadTag(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "lead_tags"

    lead_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    tag_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)

    __table_args__ = (
        UniqueConstraint("tenant_id", "lead_id", "tag_id", name="uq_lead_tags"),
        Index("ix_lead_tags_lead", "tenant_id", "lead_id"),
    )


class ContactTag(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "contact_tags"

    contact_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    tag_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)

    __table_args__ = (
        UniqueConstraint("tenant_id", "contact_id", "tag_id", name="uq_contact_tags"),
        Index("ix_contact_tags_contact", "tenant_id", "contact_id"),
    )


class OrganizationTag(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "organization_tags"

    organization_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    tag_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id", "organization_id", "tag_id", name="uq_organization_tags"
        ),
        Index("ix_organization_tags_org", "tenant_id", "organization_id"),
    )


class CrmCustomField(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "crm_custom_fields"

    entity_type: Mapped[CrmEntityType] = mapped_column(nullable=False)
    field_key: Mapped[str] = mapped_column(String(100), nullable=False)
    label: Mapped[str] = mapped_column(String(150), nullable=False)
    field_type: Mapped[CustomFieldType] = mapped_column(nullable=False)
    is_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    options: Mapped[list | None] = mapped_column(JSON, nullable=True)
    validation_rules: Mapped[dict | None] = mapped_column(JSON, nullable=True)
    position: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id",
            "entity_type",
            "field_key",
            name="uq_crm_custom_fields_key",
        ),
        Index("ix_crm_custom_fields_entity", "tenant_id", "entity_type"),
    )


class CrmCustomFieldValue(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "crm_custom_field_values"

    custom_field_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    entity_type: Mapped[CrmEntityType] = mapped_column(nullable=False)
    entity_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    value_text: Mapped[str | None] = mapped_column(Text, nullable=True)
    value_number: Mapped[Decimal | None] = mapped_column(Numeric(18, 4), nullable=True)
    value_boolean: Mapped[bool | None] = mapped_column(Boolean, nullable=True)
    value_date: Mapped[date | None] = mapped_column(Date, nullable=True)
    value_json: Mapped[list | dict | None] = mapped_column(JSON, nullable=True)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id",
            "custom_field_id",
            "entity_id",
            name="uq_crm_custom_field_values",
        ),
        Index(
            "ix_crm_custom_field_values_entity",
            "tenant_id",
            "entity_type",
            "entity_id",
        ),
    )


class CrmAddress(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    __tablename__ = "crm_addresses"

    entity_type: Mapped[CrmEntityType] = mapped_column(nullable=False)
    entity_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    address_type: Mapped[AddressType] = mapped_column(nullable=False)
    label: Mapped[str | None] = mapped_column(String(100), nullable=True)
    line1: Mapped[str] = mapped_column(String(255), nullable=False)
    line2: 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)
    postal_code: Mapped[str | None] = mapped_column(String(30), nullable=True)
    country: Mapped[str | None] = mapped_column(String(100), nullable=True)
    latitude: Mapped[Decimal | None] = mapped_column(Numeric(10, 7), nullable=True)
    longitude: Mapped[Decimal | None] = mapped_column(Numeric(10, 7), nullable=True)
    is_default: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)

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


class CrmNote(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    __tablename__ = "crm_notes"

    entity_type: Mapped[CrmEntityType] = mapped_column(nullable=False)
    entity_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    body: Mapped[str] = mapped_column(Text, nullable=False)
    body_format: Mapped[str] = mapped_column(String(20), default="richtext", nullable=False)
    version: Mapped[int] = mapped_column(Integer, default=1, nullable=False)
    is_pinned: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)

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


class CrmNoteVersion(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "crm_note_versions"

    note_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    version: Mapped[int] = mapped_column(Integer, nullable=False)
    body: Mapped[str] = mapped_column(Text, nullable=False)
    created_by: Mapped[str | None] = mapped_column(String(100), nullable=True)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id", "note_id", "version", name="uq_crm_note_versions"
        ),
        Index("ix_crm_note_versions_note", "tenant_id", "note_id"),
    )


class CrmAttachment(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    """Reference to File Storage service — no binary bytes stored in CRM."""

    __tablename__ = "crm_attachments"

    entity_type: Mapped[CrmEntityType] = mapped_column(nullable=False)
    entity_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    file_storage_id: Mapped[str] = mapped_column(String(100), nullable=False)
    file_name: Mapped[str] = mapped_column(String(255), nullable=False)
    content_type: Mapped[str | None] = mapped_column(String(150), nullable=True)
    size_bytes: Mapped[int | None] = mapped_column(Integer, nullable=True)
    kind: Mapped[AttachmentKind] = mapped_column(
        default=AttachmentKind.DOCUMENT, nullable=False
    )
    description: Mapped[str | None] = mapped_column(Text, nullable=True)

    __table_args__ = (
        Index(
            "ix_crm_attachments_entity",
            "tenant_id",
            "entity_type",
            "entity_id",
        ),
        Index("ix_crm_attachments_file", "tenant_id", "file_storage_id"),
    )


class CrmAuditLog(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "crm_audit"

    entity_type: Mapped[str] = mapped_column(String(50), nullable=False)
    entity_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    action: Mapped[AuditAction] = mapped_column(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_crm_audit_entity",
            "tenant_id",
            "entity_type",
            "entity_id",
        ),
        Index("ix_crm_audit_action", "tenant_id", "action"),
    )
