from __future__ import annotations

import logging
import time
import uuid
from dataclasses import dataclass
from typing import Any, Callable, Dict, Mapping

from app.services.session_context import extract_protocol
from app.utils.call_timeline import CallTimeline
from app.utils.logging import structured_context_from_start_data
from app.ws_gateway.call_config import build_call_config
from app.ws_gateway.session_start import (
    StartSessionContext,
    build_remote_agent_context,
    should_agent_start_conversation,
)


@dataclass(frozen=True, slots=True)
class BridgeSessionBootstrap:
    call_id_ged: str
    room_name: str
    identity: str
    token: str
    protocol: str
    phone_number: str
    call_config: Dict[str, Any]
    remote_agent_context: Dict[str, Any]
    dispatch_metadata: Dict[str, Any]
    timeline: CallTimeline


def _pick_value(payload: Mapping[str, Any] | None, *keys: str) -> str:
    if not isinstance(payload, Mapping):
        return ""

    for key in keys:
        value = payload.get(key)
        if value not in (None, ""):
            return str(value).strip()

    lowered = {str(key).lower(): value for key, value in payload.items()}
    for key in keys:
        value = lowered.get(str(key).lower())
        if value not in (None, ""):
            return str(value).strip()

    return ""


def build_bridge_session_bootstrap(
    *,
    start_ctx: StartSessionContext,
    app_env: str,
    livekit_room: str,
    default_protocol: str,
    token_factory: Callable[[str, str], str],
    logger: logging.Logger,
    now_fn: Callable[[], float] = time.time,
    uuid_factory: Callable[[], uuid.UUID] = uuid.uuid4,
    timeline_origin_unix_ms: int | None = None,
) -> BridgeSessionBootstrap:
    call_id_ged = str(start_ctx.data.get("callIdGed") or "")[:64]
    call_config = build_call_config(start_ctx.call_config)
    remote_agent_context = build_remote_agent_context(
        data=start_ctx.data,
        agent_data=start_ctx.agent_data,
    )

    base_room = (livekit_room or f"{app_env}-room").strip()
    room_name = f"{base_room}-{uuid_factory().hex[:8]}"
    identity = f"ws-bridge-{app_env}-{uuid_factory().hex[:8]}"
    token = token_factory(identity, room_name)

    protocol = (
        extract_protocol(start_ctx.payload, start_ctx.session_data)
        or (default_protocol or "").strip()
        or f"WS-{call_id_ged or 'call'}-{int(now_fn())}"
    )
    session_id = (
        _pick_value(start_ctx.data, "session_id", "sessionId")
        or _pick_value(start_ctx.agent_data, "session_id", "sessionId")
        or room_name
    )
    phone_number = str(
        start_ctx.session_data.get("gsm")
        or start_ctx.session_data.get("msisdn")
        or start_ctx.session_data.get("phone")
        or remote_agent_context.get("GSM")
        or ""
    ).strip()
    structured_context = structured_context_from_start_data(start_ctx.data)

    timeline = CallTimeline(
        logger=logger,
        component="bridge",
        timeline_id=room_name,
        protocol=protocol,
        room=room_name,
        session_id=session_id,
        phone_number=phone_number,
        origin_unix_ms=timeline_origin_unix_ms,
    )

    dispatch_metadata = {
        "env": app_env,
        "from": "ws-bridge",
        "call_id_ged": call_id_ged,
        "protocol": protocol,
        "session_id": session_id,
        "elegibility": True,
        "session_data": start_ctx.session_data,
        "intro": start_ctx.intro,
        "nudge": start_ctx.nudge,
        "agent_starts_conversation": bool(
            start_ctx.agent_starts_conversation
            or should_agent_start_conversation(start_ctx.data.get("agent"))
        ),
        "bridge_identity": identity,
        "call_config": call_config,
        "remote_agent": remote_agent_context,
        "timeline_id": room_name,
        "timeline_origin_ms": timeline.origin_unix_ms,
        "structured_log_context": structured_context.as_metadata(),
        "debug_events_enabled": start_ctx.debug_events_enabled,
    }

    return BridgeSessionBootstrap(
        call_id_ged=call_id_ged,
        room_name=room_name,
        identity=identity,
        token=token,
        protocol=protocol,
        phone_number=phone_number,
        call_config=call_config,
        remote_agent_context=remote_agent_context,
        dispatch_metadata=dispatch_metadata,
        timeline=timeline,
    )
