"""Pairing state helpers used by commander run mode."""

from __future__ import annotations

from dataclasses import replace
from datetime import datetime
from datetime import timedelta
from datetime import timezone
import secrets
from typing import Callable

from a2a_agent.config import AgentConfig
from a2a_agent.config import PendingPairing
from a2a_agent.config import ShareConfig
from a2a_agent.invite import generate_pairing_code
from a2a_agent.invite import validate_pairing_code
from a2a_agent.protocol import PairingReply


PAIRING_TTL_SECONDS = 600


def utc_now() -> datetime:
    return datetime.now(timezone.utc)


def isoformat_utc(value: datetime) -> str:
    return value.astimezone(timezone.utc).isoformat().replace("+00:00", "Z")


def parse_utc(value: str) -> datetime:
    return datetime.fromisoformat(value.replace("Z", "+00:00")).astimezone(timezone.utc)


def add_pending_pairing(
    config: AgentConfig,
    device_name: str,
    *,
    now: datetime | None = None,
    ttl_seconds: int = PAIRING_TTL_SECONDS,
) -> tuple[str, AgentConfig]:
    base_time = now or utc_now()
    active_pairings = prune_expired_pairings(config.pending_pairings, now=base_time)
    code = generate_pairing_code()
    while code in active_pairings:
        code = generate_pairing_code()

    active_pairings[code] = PendingPairing(
        device_name=device_name,
        room=config.room,
        expires_at=isoformat_utc(base_time + timedelta(seconds=ttl_seconds)),
    )
    return code, replace(config, pending_pairings=active_pairings)


def prune_expired_pairings(
    pairings: dict[str, PendingPairing],
    *,
    now: datetime | None = None,
) -> dict[str, PendingPairing]:
    base_time = now or utc_now()
    fresh: dict[str, PendingPairing] = {}
    for code, pairing in pairings.items():
        try:
            expires_at = parse_utc(pairing.expires_at)
        except ValueError:
            continue
        if expires_at > base_time:
            fresh[code] = pairing
    return fresh


def consume_pending_pairing(
    config: AgentConfig,
    code: str,
    *,
    now: datetime | None = None,
    token_factory: Callable[[], str] | None = None,
    share: ShareConfig | None = None,
) -> tuple[PairingReply, AgentConfig]:
    try:
        normalized = validate_pairing_code(code)
    except ValueError as exc:
        return PairingReply(ok=False, error=str(exc)), config

    base_time = now or utc_now()
    remaining = dict(config.pending_pairings)
    pairing = remaining.get(normalized)
    if pairing is None:
        return PairingReply(ok=False, error="pairing code not found"), config

    try:
        expires_at = parse_utc(pairing.expires_at)
    except ValueError:
        remaining.pop(normalized, None)
        updated = replace(config, pending_pairings=remaining)
        return PairingReply(ok=False, error="pairing code is invalid"), updated

    remaining.pop(normalized, None)
    updated = replace(config, pending_pairings=remaining)
    if expires_at <= base_time:
        return PairingReply(ok=False, error="pairing code expired"), updated

    token = token_factory() if token_factory is not None else secrets.token_urlsafe(32)
    reply = PairingReply(
        ok=True,
        room=pairing.room,
        device_name=pairing.device_name,
        token=token,
        broker=config.broker.to_dict(),
        # 현재 브로커는 비TLS 1883이라 이 창고 자격은 평문 MQTT로 전달된다.
        # 전용 스코프 계정이라 위험 범위를 줄였고, 추후 MQTT TLS로 보강한다.
        share=share.to_dict() if share is not None else None,
    )
    return reply, updated
