"""Short-lived Slack conversation state for Deal Desk follow-ups."""

from __future__ import annotations

from datetime import datetime, timedelta, timezone
from typing import Any, Literal

from pydantic import BaseModel, ConfigDict, Field, computed_field

from .workflow import SlackResponseTargetV1

DEFAULT_CONVERSATION_TTL_SECONDS = 30 * 60
DealDeskConversationState = Literal[
    "awaiting_deal_selection",
    "awaiting_apply_confirmation",
    "awaiting_line_selection",
    "awaiting_sku_selection",
]


class _DealDeskConversationModel(BaseModel):
    model_config = ConfigDict(extra="ignore")

    def to_dict(self) -> dict[str, Any]:
        return self.model_dump(exclude_none=False, by_alias=True)

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> "_DealDeskConversationModel":
        return cls.model_validate(data)


class DealDeskConversationCandidateV1(_DealDeskConversationModel):
    opportunity_id: str
    name: str
    account_name: str | None = None
    stage_name: str | None = None
    close_date: str | None = None
    owner_name: str | None = None
    amount: float | None = None

    @computed_field(alias="$schema")
    @property
    def schema(self) -> str:
        return "DealDeskConversationCandidate/v1"


class DealDeskConversationSelectionCandidateV1(_DealDeskConversationModel):
    requested_value: str
    label: str
    detail: str = ""
    match_tokens: list[str] = Field(default_factory=list)

    @computed_field(alias="$schema")
    @property
    def schema(self) -> str:
        return "DealDeskConversationSelectionCandidate/v1"


class DealDeskConversationSessionV1(_DealDeskConversationModel):
    version: int = 1
    channel_id: str
    thread_ts: str
    user_id: str | None = None
    org_alias: str | None = None
    state: DealDeskConversationState
    deal_query: str | None = None
    opportunity_id: str | None = None
    quote_operation: str | None = None
    quote_requested_value: str | None = None
    raw_text: str = ""
    candidates: list[DealDeskConversationCandidateV1] = Field(default_factory=list)
    selection_candidates: list[DealDeskConversationSelectionCandidateV1] = Field(default_factory=list)
    seller_slack_target: SlackResponseTargetV1 | None = None
    created_at: str
    updated_at: str
    expires_at: str
    last_event_id: str | None = None

    @computed_field(alias="$schema")
    @property
    def schema(self) -> str:
        return "DealDeskConversationSession/v1"


def build_deal_lookup_conversation_session(
    *,
    channel_id: str,
    thread_ts: str,
    user_id: str | None,
    org_alias: str | None,
    deal_query: str | None,
    quote_operation: str | None,
    quote_requested_value: str | None,
    raw_text: str,
    candidates: list[DealDeskConversationCandidateV1],
    seller_slack_target: SlackResponseTargetV1 | None,
    last_event_id: str | None = None,
    ttl_seconds: int = DEFAULT_CONVERSATION_TTL_SECONDS,
) -> DealDeskConversationSessionV1:
    created_at = _now_iso()
    return DealDeskConversationSessionV1(
        channel_id=str(channel_id or "").strip(),
        thread_ts=str(thread_ts or "").strip(),
        user_id=str(user_id or "").strip() or None,
        org_alias=str(org_alias or "").strip() or None,
        state="awaiting_deal_selection",
        deal_query=str(deal_query or "").strip() or None,
        quote_operation=str(quote_operation or "").strip() or None,
        quote_requested_value=str(quote_requested_value or "").strip() or None,
        raw_text=str(raw_text or "").strip(),
        candidates=list(candidates or []),
        seller_slack_target=seller_slack_target,
        created_at=created_at,
        updated_at=created_at,
        expires_at=_expires_at_iso(ttl_seconds),
        last_event_id=str(last_event_id or "").strip() or None,
    )


def build_apply_confirmation_conversation_session(
    *,
    channel_id: str,
    thread_ts: str,
    user_id: str | None,
    org_alias: str | None,
    opportunity_id: str,
    quote_operation: str | None,
    quote_requested_value: str | None,
    raw_text: str,
    seller_slack_target: SlackResponseTargetV1 | None,
    last_event_id: str | None = None,
    ttl_seconds: int = DEFAULT_CONVERSATION_TTL_SECONDS,
) -> DealDeskConversationSessionV1:
    created_at = _now_iso()
    return DealDeskConversationSessionV1(
        channel_id=str(channel_id or "").strip(),
        thread_ts=str(thread_ts or "").strip(),
        user_id=str(user_id or "").strip() or None,
        org_alias=str(org_alias or "").strip() or None,
        state="awaiting_apply_confirmation",
        opportunity_id=str(opportunity_id or "").strip(),
        quote_operation=str(quote_operation or "").strip() or None,
        quote_requested_value=str(quote_requested_value or "").strip() or None,
        raw_text=str(raw_text or "").strip(),
        seller_slack_target=seller_slack_target,
        created_at=created_at,
        updated_at=created_at,
        expires_at=_expires_at_iso(ttl_seconds),
        last_event_id=str(last_event_id or "").strip() or None,
    )


def build_line_item_selection_conversation_session(
    *,
    channel_id: str,
    thread_ts: str,
    user_id: str | None,
    org_alias: str | None,
    opportunity_id: str,
    quote_operation: str | None,
    raw_text: str,
    candidates: list[DealDeskConversationSelectionCandidateV1],
    seller_slack_target: SlackResponseTargetV1 | None,
    last_event_id: str | None = None,
    state: Literal["awaiting_line_selection", "awaiting_sku_selection"],
    ttl_seconds: int = DEFAULT_CONVERSATION_TTL_SECONDS,
) -> DealDeskConversationSessionV1:
    created_at = _now_iso()
    return DealDeskConversationSessionV1(
        channel_id=str(channel_id or "").strip(),
        thread_ts=str(thread_ts or "").strip(),
        user_id=str(user_id or "").strip() or None,
        org_alias=str(org_alias or "").strip() or None,
        state=state,
        opportunity_id=str(opportunity_id or "").strip(),
        quote_operation=str(quote_operation or "").strip() or None,
        raw_text=str(raw_text or "").strip(),
        selection_candidates=list(candidates or []),
        seller_slack_target=seller_slack_target,
        created_at=created_at,
        updated_at=created_at,
        expires_at=_expires_at_iso(ttl_seconds),
        last_event_id=str(last_event_id or "").strip() or None,
    )


def _now_iso() -> str:
    return datetime.now(timezone.utc).isoformat()


def _expires_at_iso(ttl_seconds: int) -> str:
    ttl = max(int(ttl_seconds or 0), 60)
    return (datetime.now(timezone.utc) + timedelta(seconds=ttl)).isoformat()


__all__ = [
    "DEFAULT_CONVERSATION_TTL_SECONDS",
    "DealDeskConversationCandidateV1",
    "DealDeskConversationSelectionCandidateV1",
    "DealDeskConversationSessionV1",
    "DealDeskConversationState",
    "build_apply_confirmation_conversation_session",
    "build_deal_lookup_conversation_session",
    "build_line_item_selection_conversation_session",
]
