"""
backend/services/auto_mode_matcher.py - Post-session lead matching for Sentinel auto mode.
"""

from __future__ import annotations

import uuid
from dataclasses import dataclass
from datetime import datetime, timezone
from typing import Any

import asyncpg


@dataclass
class AutoModeMatchResult:
    action: str
    lead_id: str
    confidence: float
    rationale: str
    tags_applied: list[str]


def _normalise_plate(plate: str | None) -> str | None:
    if not plate:
        return None
    cleaned = "".join(ch for ch in plate.upper() if ch.isalnum())
    return cleaned or None


async def _find_match_by_plate(
    conn: asyncpg.Connection,
    normalized_plate: str,
) -> tuple[str, float, str] | None:
    row = await conn.fetchrow(
        """
        SELECT linked_lead_id::text AS lead_id
        FROM cctv_events
        WHERE regexp_replace(COALESCE(license_plate, ''), '[^A-Za-z0-9]', '', 'g') = $1
          AND linked_lead_id IS NOT NULL
        ORDER BY captured_at DESC
        LIMIT 1
        """,
        normalized_plate,
    )
    if row:
        return row["lead_id"], 0.96, "matched_existing_plate"
    return None


async def _find_match_by_tags(
    conn: asyncpg.Connection,
    tags: list[str],
) -> tuple[str, float, str] | None:
    if not tags:
        return None
    row = await conn.fetchrow(
        """
        SELECT id::text AS lead_id,
               COALESCE(cardinality(tags & $1::text[]), 0) AS overlap,
               last_active
        FROM leads_intelligence
        WHERE tags && $1::text[]
          AND status IN ('engaged', 'qualified', 'hot')
        ORDER BY overlap DESC, last_active DESC
        LIMIT 1
        """,
        tags,
    )
    if row and row["overlap"] > 0:
        confidence = min(0.65 + (0.1 * int(row["overlap"])), 0.85)
        return row["lead_id"], confidence, "matched_tag_overlap"
    return None


async def _create_auto_lead(
    conn: asyncpg.Connection,
    *,
    wealth_indicator: str,
    tags: list[str],
    session_id: str,
) -> str:
    name = f"Auto Visitor {datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M')}"
    qualification = "whale" if wealth_indicator == "HNI" else "potential"
    row = await conn.fetchrow(
        """
        INSERT INTO leads_intelligence
          (name, source, status, qualification, quantum_dynamics_score, tags, last_message)
        VALUES
          ($1, 'walkin', 'new', $2::qualification_enum, 50, $3::text[], $4)
        RETURNING id::text
        """,
        name,
        qualification,
        tags,
        f"Auto-created from Sentinel auto mode session {session_id}",
    )
    return row["id"]


async def auto_mode_match_session(
    conn: asyncpg.Connection,
    *,
    session_id: str,
) -> AutoModeMatchResult:
    session = await conn.fetchrow(
        """
        SELECT id::text, lead_id::text, session_mode, auto_mode_evidence, final_qd_score
        FROM perception_sessions
        WHERE id = $1::uuid
        """,
        session_id,
    )
    if not session:
        raise ValueError(f"Session {session_id} not found.")
    if session["session_mode"] != "auto":
        raise ValueError("auto_mode_match_session can only be used for auto sessions.")
    if session["lead_id"]:
        return AutoModeMatchResult(
            action="already_linked",
            lead_id=session["lead_id"],
            confidence=1.0,
            rationale="session_already_has_lead",
            tags_applied=[],
        )

    evidence: dict[str, Any] = dict(session["auto_mode_evidence"] or {})
    normalized_plate = _normalise_plate(evidence.get("license_plate"))
    inferred_tags = list(dict.fromkeys((evidence.get("tags") or []) + (evidence.get("nemoclaw_tags") or [])))
    wealth_indicator = str(evidence.get("wealth_indicator") or "unknown")

    match: tuple[str, float, str] | None = None
    if normalized_plate:
        match = await _find_match_by_plate(conn, normalized_plate)
    if not match:
        match = await _find_match_by_tags(conn, inferred_tags)

    action = "linked_existing" if match else "created_new"
    if match:
        lead_id, confidence, rationale = match
    else:
        lead_id = await _create_auto_lead(
            conn,
            wealth_indicator=wealth_indicator,
            tags=inferred_tags,
            session_id=session_id,
        )
        confidence = 0.55
        rationale = "created_new_from_auto_mode"

    await conn.execute(
        """
        UPDATE perception_sessions
        SET lead_id = $1::uuid,
            auto_mode_matched_at = NOW(),
            auto_mode_evidence = auto_mode_evidence || $2::jsonb
        WHERE id = $3::uuid
        """,
        lead_id,
        {
            "match_action": action,
            "match_confidence": confidence,
            "match_rationale": rationale,
        },
        session_id,
    )

    await conn.execute(
        """
        UPDATE cctv_events
        SET linked_lead_id = $1::uuid
        WHERE linked_session_id = $2::uuid
          AND linked_lead_id IS NULL
        """,
        lead_id,
        session_id,
    )

    if inferred_tags:
        await conn.execute(
            """
            UPDATE leads_intelligence
            SET tags = ARRAY(
                SELECT DISTINCT unnest(COALESCE(tags, ARRAY[]::text[]) || $1::text[])
            ),
                quantum_dynamics_score = COALESCE($2, quantum_dynamics_score),
                updated_at = NOW()
            WHERE id = $3::uuid
            """,
            inferred_tags,
            session["final_qd_score"],
            lead_id,
        )

    await conn.execute(
        """
        INSERT INTO omnichannel_logs (event_type, lead_id, payload)
        VALUES ('LEAD_TAGGED', $1::uuid, $2::jsonb)
        """,
        lead_id,
        {
            "source": "auto_mode_matcher",
            "session_id": session_id,
            "tags_added": inferred_tags,
            "match_action": action,
            "match_confidence": confidence,
            "match_rationale": rationale,
        },
    )

    return AutoModeMatchResult(
        action=action,
        lead_id=lead_id,
        confidence=confidence,
        rationale=rationale,
        tags_applied=inferred_tags,
    )
