"""Phase 6.3 — Enterprise Sales Collaboration Platform models."""
from __future__ import annotations

import uuid
from datetime import datetime

from sqlalchemy import (
    Boolean,
    DateTime,
    Index,
    Integer,
    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 (
    CallDirection,
    CollaborationEntityType,
    EmailDeliveryStatus,
    GUID,
    RecurrenceRule,
    TeamRoleCode,
    TimelineEventType,
)


class ActivityTypeDefinition(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "activity_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_activity_types_tenant_code"),
    )


class ActivityStatusDefinition(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "activity_statuses"

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

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


class ActivityPriorityDefinition(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "activity_priorities"

    code: Mapped[str] = mapped_column(String(50), nullable=False)
    name: Mapped[str] = mapped_column(String(150), nullable=False)
    rank: 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", "code", name="uq_activity_priorities_tenant_code"
        ),
    )


class ActivityParticipant(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "activity_participants"

    activity_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    user_id: Mapped[str | None] = mapped_column(String(100), nullable=True)
    contact_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    role_label: Mapped[str | None] = mapped_column(String(100), nullable=True)
    is_required: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)

    __table_args__ = (
        Index("ix_activity_participants_activity", "tenant_id", "activity_id"),
    )


class ActivityReminder(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "activity_reminders"

    activity_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    remind_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
    channel: Mapped[str] = mapped_column(String(50), default="in_app", nullable=False)
    is_sent: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)

    __table_args__ = (
        Index("ix_activity_reminders_activity", "tenant_id", "activity_id"),
    )


class SalesTask(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin, SoftDeleteMixin):
    __tablename__ = "tasks"

    activity_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    title: Mapped[str] = mapped_column(String(255), nullable=False)
    description: Mapped[str | None] = mapped_column(Text, nullable=True)
    owner_user_id: Mapped[str | None] = mapped_column(String(100), nullable=True)
    assignee_user_id: Mapped[str | None] = mapped_column(String(100), nullable=True)
    status: Mapped[str] = mapped_column(String(30), default="open", nullable=False)
    priority: Mapped[str] = mapped_column(String(20), default="medium", nullable=False)
    due_at: Mapped[datetime | None] = mapped_column(
        DateTime(timezone=True), nullable=True
    )
    completed_at: Mapped[datetime | None] = mapped_column(
        DateTime(timezone=True), nullable=True
    )
    recurrence: Mapped[RecurrenceRule] = mapped_column(
        default=RecurrenceRule.NONE, nullable=False
    )
    opportunity_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)
    completion_percent: Mapped[int] = mapped_column(Integer, default=0, nullable=False)

    __table_args__ = (
        Index("ix_tasks_tenant_status", "tenant_id", "status"),
        Index("ix_tasks_assignee", "tenant_id", "assignee_user_id"),
    )


class TaskChecklistItem(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "task_checklists"

    task_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    title: Mapped[str] = mapped_column(String(255), nullable=False)
    position: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    is_completed: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    completed_at: Mapped[datetime | None] = mapped_column(
        DateTime(timezone=True), nullable=True
    )

    __table_args__ = (Index("ix_task_checklists_task", "tenant_id", "task_id"),)


class Meeting(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "meetings"

    activity_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    subject: Mapped[str] = mapped_column(String(255), nullable=False)
    start_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
    end_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
    location: Mapped[str | None] = mapped_column(String(255), nullable=True)
    online_meeting_url: Mapped[str | None] = mapped_column(String(500), nullable=True)
    agenda: Mapped[str | None] = mapped_column(Text, nullable=True)
    notes: Mapped[str | None] = mapped_column(Text, nullable=True)
    outcome: Mapped[str | None] = mapped_column(Text, nullable=True)
    owner_user_id: Mapped[str | None] = mapped_column(String(100), nullable=True)
    opportunity_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)
    is_finished: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    finished_at: Mapped[datetime | None] = mapped_column(
        DateTime(timezone=True), nullable=True
    )

    __table_args__ = (
        Index("ix_meetings_tenant_start", "tenant_id", "start_at"),
        Index("ix_meetings_opportunity", "tenant_id", "opportunity_id"),
    )


class MeetingAttendee(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "meeting_attendees"

    meeting_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    user_id: Mapped[str | None] = mapped_column(String(100), nullable=True)
    contact_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    email: Mapped[str | None] = mapped_column(String(255), nullable=True)
    display_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
    response_status: Mapped[str] = mapped_column(
        String(30), default="pending", nullable=False
    )

    __table_args__ = (Index("ix_meeting_attendees_meeting", "tenant_id", "meeting_id"),)


class CallOutcome(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "call_outcomes"

    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_call_outcomes_tenant_code"),
    )


class CallLog(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "call_logs"

    activity_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    direction: Mapped[CallDirection] = mapped_column(nullable=False)
    subject: Mapped[str | None] = mapped_column(String(255), nullable=True)
    duration_seconds: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    outcome_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    outcome_label: Mapped[str | None] = mapped_column(String(150), nullable=True)
    recording_ref: Mapped[str | None] = mapped_column(String(255), nullable=True)
    notes: Mapped[str | None] = mapped_column(Text, nullable=True)
    owner_user_id: Mapped[str | None] = mapped_column(String(100), nullable=True)
    opportunity_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)
    organization_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    called_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)

    __table_args__ = (
        Index("ix_call_logs_opportunity", "tenant_id", "opportunity_id"),
        Index("ix_call_logs_called_at", "tenant_id", "called_at"),
    )


class EmailThread(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "email_threads"

    subject: Mapped[str] = mapped_column(String(255), nullable=False)
    opportunity_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)
    lead_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    last_message_at: Mapped[datetime | None] = mapped_column(
        DateTime(timezone=True), nullable=True
    )

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


class EmailMessage(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    """CRM email metadata only — delivery owned by Notification Service."""

    __tablename__ = "emails"

    thread_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    subject: Mapped[str] = mapped_column(String(255), nullable=False)
    status: Mapped[EmailDeliveryStatus] = mapped_column(
        default=EmailDeliveryStatus.DRAFT, nullable=False
    )
    opened: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    clicked: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    opportunity_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    notification_ref: Mapped[str | None] = mapped_column(String(100), nullable=True)
    sent_at: Mapped[datetime | None] = mapped_column(
        DateTime(timezone=True), nullable=True
    )

    __table_args__ = (Index("ix_emails_thread", "tenant_id", "thread_id"),)


class SalesTimelineEvent(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    """Immutable sales timeline entries."""

    __tablename__ = "sales_timeline"

    event_type: Mapped[TimelineEventType] = mapped_column(nullable=False)
    entity_type: Mapped[CollaborationEntityType] = mapped_column(nullable=False)
    entity_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    related_opportunity_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    related_lead_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    related_contact_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    related_organization_id: Mapped[uuid.UUID | None] = mapped_column(
        GUID(), nullable=True
    )
    actor_user_id: Mapped[str | None] = mapped_column(String(100), nullable=True)
    title: Mapped[str] = mapped_column(String(255), nullable=False)
    payload: Mapped[dict | None] = mapped_column(JSON, nullable=True)
    occurred_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), nullable=False
    )
    is_immutable: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)

    __table_args__ = (
        Index("ix_sales_timeline_entity", "tenant_id", "entity_type", "entity_id"),
        Index("ix_sales_timeline_occurred", "tenant_id", "occurred_at"),
        Index("ix_sales_timeline_opportunity", "tenant_id", "related_opportunity_id"),
    )


class TimelineEventLink(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    """Optional secondary index rows for timeline fan-out (same immutable event)."""

    __tablename__ = "timeline_events"

    timeline_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    link_entity_type: Mapped[CollaborationEntityType] = mapped_column(nullable=False)
    link_entity_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)

    __table_args__ = (
        Index("ix_timeline_events_link", "tenant_id", "link_entity_type", "link_entity_id"),
        Index("ix_timeline_events_timeline", "tenant_id", "timeline_id"),
    )


class Comment(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    __tablename__ = "comments"

    entity_type: Mapped[CollaborationEntityType] = mapped_column(nullable=False)
    entity_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    parent_comment_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    body: Mapped[str] = mapped_column(Text, nullable=False)
    body_format: Mapped[str] = mapped_column(String(30), 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_comments_entity", "tenant_id", "entity_type", "entity_id"),
        Index("ix_comments_parent", "tenant_id", "parent_comment_id"),
    )


class CommentVersion(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "comment_versions"

    comment_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)
    edited_by: Mapped[str | None] = mapped_column(String(100), nullable=True)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id", "comment_id", "version", name="uq_comment_versions"
        ),
    )


class Mention(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "mentions"

    comment_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    entity_type: Mapped[CollaborationEntityType] = mapped_column(nullable=False)
    entity_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    mentioned_user_id: Mapped[str] = mapped_column(String(100), nullable=False)
    mentioned_by: Mapped[str | None] = mapped_column(String(100), nullable=True)
    is_read: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    notification_event_emitted: Mapped[bool] = mapped_column(
        Boolean, default=False, nullable=False
    )

    __table_args__ = (
        Index("ix_mentions_user", "tenant_id", "mentioned_user_id", "is_read"),
        Index("ix_mentions_entity", "tenant_id", "entity_type", "entity_id"),
    )


class Bookmark(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "bookmarks"

    user_id: Mapped[str] = mapped_column(String(100), nullable=False)
    entity_type: Mapped[CollaborationEntityType] = mapped_column(nullable=False)
    entity_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    label: Mapped[str | None] = mapped_column(String(150), nullable=True)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id",
            "user_id",
            "entity_type",
            "entity_id",
            name="uq_bookmarks_user_entity",
        ),
    )


class Favorite(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "favorites"

    user_id: Mapped[str] = mapped_column(String(100), nullable=False)
    entity_type: Mapped[CollaborationEntityType] = mapped_column(nullable=False)
    entity_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id",
            "user_id",
            "entity_type",
            "entity_id",
            name="uq_favorites_user_entity",
        ),
    )


class TeamRole(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "team_roles"

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

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


class SalesTeamMember(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "sales_team_members"

    user_id: Mapped[str] = mapped_column(String(100), nullable=False)
    display_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
    team_key: Mapped[str] = mapped_column(String(100), nullable=False)
    role_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    role_code: Mapped[TeamRoleCode] = mapped_column(
        default=TeamRoleCode.SALESPERSON, nullable=False
    )
    is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id", "team_key", "user_id", name="uq_sales_team_members"
        ),
        Index("ix_sales_team_members_team", "tenant_id", "team_key"),
    )


class TeamAssignment(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "team_assignments"

    team_key: Mapped[str] = mapped_column(String(100), nullable=False)
    member_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    entity_type: Mapped[CollaborationEntityType] = mapped_column(nullable=False)
    entity_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    assigned_by: Mapped[str | None] = mapped_column(String(100), nullable=True)

    __table_args__ = (
        Index("ix_team_assignments_entity", "tenant_id", "entity_type", "entity_id"),
        Index("ix_team_assignments_team", "tenant_id", "team_key"),
    )


class CrmNotificationPreference(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    """In-CRM preference flags only — delivery owned by Notification Platform."""

    __tablename__ = "crm_notifications_preferences"

    user_id: Mapped[str] = mapped_column(String(100), nullable=False)
    mentions_enabled: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
    task_reminders_enabled: Mapped[bool] = mapped_column(
        Boolean, default=True, nullable=False
    )
    meeting_reminders_enabled: Mapped[bool] = mapped_column(
        Boolean, default=True, nullable=False
    )
    timeline_digest_enabled: Mapped[bool] = mapped_column(
        Boolean, default=False, nullable=False
    )

    __table_args__ = (
        UniqueConstraint(
            "tenant_id", "user_id", name="uq_crm_notification_prefs_user"
        ),
    )


class SalesCollaborationAudit(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "sales_collaboration_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_collab_audit_entity",
            "tenant_id",
            "entity_type",
            "entity_id",
        ),
    )
