from __future__ import annotations

import ctypes
import hashlib
import json
import os
import select
import struct
import sys
from datetime import datetime, timezone
from pathlib import Path

from gateway.platforms.telegram_customer_bootstrap import BootstrapState, RoomBootstrapStore, room_bootstrap_state_dir


def write_private(path: Path, payload: object) -> None:
    temporary = path.with_name(f".{path.name}.{os.getpid()}")
    descriptor = os.open(temporary, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_CLOEXEC, 0o600)
    try:
        with os.fdopen(descriptor, "w", encoding="utf-8") as stream:
            json.dump(payload, stream, sort_keys=True, separators=(",", ":"))
            stream.write("\n")
            stream.flush()
            os.fsync(stream.fileno())
        os.replace(temporary, path)
        path.chmod(0o600)
    finally:
        if temporary.exists():
            temporary.unlink()


def main() -> int:
    profile, evidence, session_id = Path(sys.argv[1]), Path(sys.argv[2]), sys.argv[3]
    prepared = json.loads((evidence / "replacement-preparation-receipt.json").read_text())
    expires_at = datetime.fromisoformat(prepared["session"]["expires_at"]).astimezone(timezone.utc)
    store = RoomBootstrapStore(room_bootstrap_state_dir(profile))
    session = store.get(session_id)
    if session.state is not BootstrapState.PREPARED or session.generation != 1:
        return 2
    libc = ctypes.CDLL(None, use_errno=True)
    descriptor = libc.inotify_init1(os.O_CLOEXEC)
    if descriptor < 0:
        raise OSError(ctypes.get_errno(), "inotify_init1")
    try:
        if libc.inotify_add_watch(descriptor, os.fsencode(store.state_dir), 0x8 | 0x80) < 0:
            raise OSError(ctypes.get_errno(), "inotify_add_watch")
        write_private(evidence / "replacement-claim-watch-ready.json", {
            "schema": "task26-replacement-claim-watch-ready-v1",
            "candidate_digest": prepared["candidate"]["full_candidate_digest"],
            "session_id": session_id,
            "state": session.state.value,
            "generation": session.generation,
            "expires_at": expires_at.isoformat(),
            "method": "inotify moved_to/close_write on canonical ledger directory",
            "token_exposed": False,
        })
        while True:
            remaining = (expires_at - datetime.now(timezone.utc)).total_seconds()
            if remaining <= 0:
                write_private(evidence / "replacement-claim-transition-receipt.json", {
                    "schema": "task26-replacement-claim-transition-v1",
                    "candidate_digest": prepared["candidate"]["full_candidate_digest"],
                    "session_id": session_id,
                    "result": "INVITE_EXPIRED_WITHOUT_OBSERVED_CLAIM",
                })
                return 3
            readable, _, _ = select.select([descriptor], [], [], remaining)
            if not readable:
                continue
            data, offset, changed = os.read(descriptor, 65536), 0, False
            while offset + 16 <= len(data):
                _, _, _, length = struct.unpack_from("iIII", data, offset)
                name = data[offset + 16:offset + 16 + length].split(b"\0", 1)[0]
                offset += 16 + length
                changed = changed or name == b"ledger.json"
            if not changed:
                continue
            current = store.get(session_id)
            if current.state is BootstrapState.PREPARED:
                continue
            registry = json.loads((profile / "customers" / "registry.json").read_text())
            write_private(evidence / "replacement-claim-transition-receipt.json", {
                "schema": "task26-replacement-claim-transition-v1",
                "candidate_digest": prepared["candidate"]["full_candidate_digest"],
                "session_id": session_id,
                "result": "STATE_TRANSITION_OBSERVED",
                "state": current.state.value,
                "generation": current.generation,
                "ledger_sha256": hashlib.sha256(store.ledger_path.read_bytes()).hexdigest(),
                "registry_customer_count": len(registry.get("customers", [])),
                "observed_at": datetime.now(timezone.utc).isoformat(),
                "token_exposed": False,
            })
            return 0
    finally:
        os.close(descriptor)


if __name__ == "__main__":
    raise SystemExit(main())
