from __future__ import annotations

from dataclasses import dataclass
from typing import Any, Dict, Iterable, Literal, Optional

SellerPosture = Literal["advisor", "peer", "authority", "gatekeeper"]
DealClass = Literal["routine", "premium", "speculative", "aspirational"]
TensionStage = Literal["status", "disruption", "authority", "possibility", "commitment"]
DisagreementLevel = Literal["low", "medium", "high"]
NarrativeMode = Literal["none", "compressed_analogy", "time_asymmetry"]
FrictionMode = Literal["off", "soft", "firm"]
InfoAsymmetry = Literal["none", "light", "strong"]
DecisionPressure = Literal["low", "medium", "high"]


@dataclass(frozen=True)
class StatusDirective:
    seller_posture: SellerPosture
    deal_class: DealClass
    tension_stage: TensionStage
    disagreement_level: DisagreementLevel
    narrative_mode: NarrativeMode
    friction_mode: FrictionMode
    info_asymmetry: InfoAsymmetry
    decision_pressure: DecisionPressure


_SPECULATIVE_KEYWORDS = [
    "invest",
    "investment",
    "speculative",
    "speculation",
    "alpha",
    "upside",
    "return",
    "returns",
    "roi",
    "flip",
    "resale",
    "portfolio",
    "private equity",
    "venture",
    "fund",
    "yield",
    "arbitrage",
]

_ASPIRATION_KEYWORDS = [
    "luxury",
    "exclusive",
    "elite",
    "status",
    "prestige",
    "statement piece",
    "iconic",
    "flagship",
    "signature",
    "dream",
    "once in a lifetime",
    "aspirational",
]

_PREMIUM_KEYWORDS = [
    "premium",
    "high end",
    "high-end",
    "top tier",
    "top-tier",
    "white glove",
    "concierge",
]

_PERFORMANCE_KEYWORDS = [
    "performance",
    "track",
    "track-ready",
    "horsepower",
    "supercar",
    "sports car",
    "motorsport",
]


def _has_any(text: str, keywords: Iterable[str]) -> bool:
    if not text:
        return False
    t = text.lower()
    return any(k in t for k in keywords)


def classify_deal(
    memory: Dict[str, Any],
    user_message: str,
    budget_amount: Optional[int],
    high_ticket_threshold: int,
    vertical: Optional[str] = None,
) -> DealClass:
    combined = " ".join(
        [
            str(user_message or ""),
            str(memory.get("use_case") or ""),
            str(memory.get("product") or ""),
            str(memory.get("industry") or ""),
        ]
    )

    if _has_any(combined, _SPECULATIVE_KEYWORDS):
        return "speculative"

    if _has_any(combined, _ASPIRATION_KEYWORDS):
        return "aspirational"

    if vertical in ("art_luxury", "real_estate"):
        return "aspirational"

    if _has_any(combined, _PERFORMANCE_KEYWORDS):
        return "premium"

    if _has_any(combined, _PREMIUM_KEYWORDS):
        return "premium"

    if budget_amount and budget_amount >= high_ticket_threshold:
        return "premium"

    offer = str(memory.get("offer_category") or "").lower()
    if offer in ("art", "art_luxury"):
        return "aspirational"

    return "routine"


def choose_seller_posture(
    deal_class: DealClass,
    buyer_role: Optional[str],
) -> SellerPosture:
    role = (buyer_role or "").lower()
    senior_buyer = any(
        k in role for k in ["founder", "owner", "ceo", "chief", "president", "partner", "principal", "chair"]
    )
    if deal_class == "speculative":
        return "gatekeeper"
    if deal_class in ("premium", "aspirational"):
        return "authority"
    if senior_buyer:
        return "peer"
    return "advisor"


def next_tension_stage(
    current: Optional[TensionStage],
    signals: Iterable[str],
    memory: Dict[str, Any],
    posture: SellerPosture,
    sales_state: str,
) -> TensionStage:
    stage = current or "status"
    signals_set = set(signals or [])

    if memory.get("committed") or "close_intent" in signals_set:
        return "commitment"

    # Non-linear resets when friction or skepticism appear.
    if signals_set.intersection({"stall", "trust", "price", "price_control"}):
        return "authority" if posture in ("authority", "gatekeeper") else "disruption"

    if sales_state in ("NEXT_STEP", "HANDOFF"):
        return "commitment"

    if stage == "status":
        return "disruption"
    if stage == "disruption":
        if memory.get("use_case") or memory.get("key_pain") or sales_state in ("VALUE", "OBJECTIONS"):
            return "authority"
        return "disruption"
    if stage == "authority":
        if memory.get("use_case") or memory.get("key_pain"):
            return "possibility"
        return "authority"
    if stage == "possibility":
        return "possibility"
    return stage


def choose_disagreement_level(posture: SellerPosture) -> DisagreementLevel:
    if posture == "advisor":
        return "low"
    if posture == "peer":
        return "medium"
    if posture == "authority":
        return "medium"
    return "high"


def choose_narrative_mode(deal_class: DealClass, tension_stage: TensionStage) -> NarrativeMode:
    if deal_class in ("premium", "speculative", "aspirational"):
        if tension_stage in ("disruption", "authority"):
            return "time_asymmetry"
        if tension_stage == "possibility":
            return "compressed_analogy"
    return "none"


def choose_friction_mode(posture: SellerPosture, tension_stage: TensionStage) -> FrictionMode:
    if posture in ("authority", "gatekeeper") and tension_stage in ("status", "disruption", "authority"):
        return "firm" if posture == "gatekeeper" else "soft"
    return "off"


def choose_info_asymmetry(posture: SellerPosture) -> InfoAsymmetry:
    if posture == "gatekeeper":
        return "strong"
    if posture == "authority":
        return "light"
    return "none"


def choose_decision_pressure(
    deal_class: DealClass,
    sales_state: str,
    signals: Iterable[str],
) -> DecisionPressure:
    base = {
        "GREETING": "low",
        "DISCOVERY": "low",
        "QUALIFICATION": "medium",
        "VALUE": "medium",
        "OBJECTIONS": "medium",
        "NEXT_STEP": "high",
        "HANDOFF": "high",
    }.get(sales_state, "medium")

    if "close_intent" in set(signals or []):
        return "high"

    if deal_class in ("premium", "speculative", "aspirational"):
        if base == "low":
            return "medium"
        if base == "medium":
            return "high"
    return base


def build_status_directive(
    deal_class: DealClass,
    tension_stage: TensionStage,
    posture: SellerPosture,
    decision_pressure: DecisionPressure,
) -> StatusDirective:
    return StatusDirective(
        seller_posture=posture,
        deal_class=deal_class,
        tension_stage=tension_stage,
        disagreement_level=choose_disagreement_level(posture),
        narrative_mode=choose_narrative_mode(deal_class, tension_stage),
        friction_mode=choose_friction_mode(posture, tension_stage),
        info_asymmetry=choose_info_asymmetry(posture),
        decision_pressure=decision_pressure,
    )
