"""One-use prepare and read-only verification operations."""

from __future__ import annotations

import hashlib
import json
import os
import stat
from collections.abc import Mapping
from datetime import UTC, datetime
from pathlib import Path
from urllib.parse import parse_qs, urlparse

from gateway.platforms import telegram_customer_bootstrap as bootstrap

from first_customer_invite_contract import (
    BOT_USERNAME,
    CANDIDATE,
    FORBIDDEN_ROOTS,
    OWNER_ID,
    ControllerError,
    JsonValue,
    clean_baseline,
    exclusive_json,
    json_object,
    require_private_file,
    sha256_file,
)


def authenticated_ledger(profile: Path) -> tuple[dict[str, JsonValue], Path]:
    path = profile / FORBIDDEN_ROOTS[0] / "ledger.json"
    value = json_object(path)
    sessions = value.get("sessions")
    if value.get("schema") != "telegram-customer-bootstrap-v1" or not isinstance(
        sessions, list
    ):
        raise ControllerError("bootstrap ledger is malformed")
    raw = json.dumps(
        {"schema": value["schema"], "sessions": sessions},
        ensure_ascii=False,
        sort_keys=True,
        separators=(",", ":"),
        allow_nan=False,
    ).encode()
    if value.get("digest") != hashlib.sha256(raw).hexdigest():
        raise ControllerError("bootstrap ledger digest mismatch")
    return value, path


def _reserve_private(path: Path) -> int:
    if (
        path.parent.is_symlink()
        or not path.parent.is_dir()
        or stat.S_IMODE(path.parent.stat().st_mode) != 0o700
    ):
        raise ControllerError("output directory is not private")
    return os.open(
        path,
        os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_CLOEXEC | os.O_NOFOLLOW,
        0o600,
    )


def _write_reserved(descriptor: int, value: Mapping[str, object]) -> None:
    raw = (
        json.dumps(value, ensure_ascii=True, sort_keys=True, separators=(",", ":"))
        + "\n"
    ).encode()
    os.lseek(descriptor, 0, os.SEEK_SET)
    os.ftruncate(descriptor, 0)
    os.fchmod(descriptor, 0o600)
    os.write(descriptor, raw)
    os.fsync(descriptor)


def prepare_invite(
    profile: Path,
    draft_path: Path,
    handoff: Path,
    receipt: Path,
) -> None:
    draft = clean_baseline(profile, draft_path)
    handoff_descriptor = _reserve_private(handoff)
    try:
        receipt_descriptor = _reserve_private(receipt)
    except OSError:
        os.close(handoff_descriptor)
        handoff.unlink()
        raise
    invite_created = False
    try:
        reserved = {"schema": "dualcoach-private-output-reservation-v1"}
        _write_reserved(handoff_descriptor, reserved)
        _write_reserved(receipt_descriptor, reserved)
        store = bootstrap.RoomBootstrapStore(
            bootstrap.room_bootstrap_state_dir(profile)
        )
        prepared = store.prepare_rehearsal_customer_invite(
            draft,
            bot_username=BOT_USERNAME,
            owner_id=OWNER_ID,
        )
        invite_created = True
        tokens = parse_qs(urlparse(prepared.customer_link).query).get("start")
        if tokens is None or len(tokens) != 1 or not tokens[0].startswith("rc1_"):
            raise ControllerError("private customer link is invalid")
        token = tokens[0]
        if prepared.session.sid_hash != hashlib.sha256(token[4:].encode()).hexdigest():
            raise ControllerError("invite token hash mismatch")
        _write_reserved(
            handoff_descriptor,
            {
                "schema": "dualcoach-first-customer-private-handoff-v1",
                "session_id": prepared.session.session_id,
                "sid_hash": prepared.session.sid_hash,
                "start_token": token,
                "customer_link": prepared.customer_link,
                "expires_at": prepared.expires_at.isoformat(),
            },
        )
        ledger, ledger_path = authenticated_ledger(profile)
        sessions = ledger["sessions"]
        expected_lifetime = prepared.expires_at - prepared.session.created_at
        if not isinstance(sessions, list) or len(sessions) != 1:
            raise ControllerError("exactly one invite session was not created")
        session = sessions[0]
        customer_draft = (
            session.get("customer_draft") if isinstance(session, dict) else None
        )
        if (
            not isinstance(session, dict)
            or session.get("session_id") != prepared.session.session_id
            or session.get("sid_hash") != prepared.session.sid_hash
            or session.get("state") != "PREPARED"
            or session.get("generation") != 1
            or session.get("role_claims") != []
            or not isinstance(customer_draft, dict)
            or customer_draft.get("customer_user_id") is not None
            or int(expected_lifetime.total_seconds()) != 86_400
        ):
            raise ControllerError("prepared invite postcondition mismatch")
        _write_reserved(
            receipt_descriptor,
            {
                "schema": "dualcoach-first-customer-prepare-receipt-v1",
                "status": "PASS_PREPARED",
                "candidate_digest": CANDIDATE,
                "session_id": prepared.session.session_id,
                "sid_hash": prepared.session.sid_hash,
                "state": "PREPARED",
                "generation": 1,
                "created_at": prepared.session.created_at.isoformat(),
                "expires_at": prepared.expires_at.isoformat(),
                "ttl_seconds": 86_400,
                "ledger_sha256": sha256_file(ledger_path),
                "handoff_sha256": sha256_file(handoff),
                "raw_token_locations": [str(handoff)],
            },
        )
    finally:
        os.close(handoff_descriptor)
        os.close(receipt_descriptor)
        if not invite_created:
            handoff.unlink(missing_ok=True)
            receipt.unlink(missing_ok=True)


def verify_invite(
    profile: Path,
    handoff: Path,
    session_id: str,
    sid_hash: str,
    receipt: Path,
) -> None:
    require_private_file(handoff)
    before, path = authenticated_ledger(profile)
    ledger_sha256 = sha256_file(path)
    sessions = before["sessions"]
    if not isinstance(sessions, list):
        raise ControllerError("bootstrap sessions are malformed")
    matches = [
        item
        for item in sessions
        if isinstance(item, dict) and item.get("session_id") == session_id
    ]
    private = json_object(handoff)
    token = private.get("start_token")
    if (
        len(matches) != 1
        or not isinstance(token, str)
        or not token.startswith("rc1_")
        or hashlib.sha256(token[4:].encode()).hexdigest() != sid_hash
        or private.get("sid_hash") != sid_hash
    ):
        raise ControllerError("private handoff binding mismatch")
    session = matches[0]
    draft = session.get("customer_draft")
    expires_at = datetime.fromisoformat(str(session["expires_at"]))
    available = (
        session.get("sid_hash") == sid_hash
        and session.get("state") == "PREPARED"
        and session.get("generation") == 1
        and session.get("role_claims") == []
        and isinstance(draft, dict)
        and draft.get("customer_user_id") is None
        and datetime.now(UTC) < expires_at
    )
    if not available or sha256_file(path) != ledger_sha256:
        raise ControllerError("invite is unavailable or verification mutated authority")
    exclusive_json(
        receipt,
        {
            "schema": "dualcoach-first-customer-verify-receipt-v1",
            "status": "PASS_AVAILABLE",
            "candidate_digest": CANDIDATE,
            "session_id": session_id,
            "sid_hash": sid_hash,
            "state": "PREPARED",
            "generation": 1,
            "expires_at": expires_at.isoformat(),
            "ledger_sha256": ledger_sha256,
            "mutations": 0,
        },
    )
