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 sha256(path: Path) -> str:
    return hashlib.sha256(path.read_bytes()).hexdigest()


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 = Path(sys.argv[1])
    evidence = Path(sys.argv[2])
    session_id = sys.argv[3]
    preparation = evidence / "preparation-receipt.json"
    prepared = json.loads(preparation.read_text(encoding="utf-8"))
    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:
        watch = libc.inotify_add_watch(
            descriptor,
            os.fsencode(store.state_dir),
            0x00000008 | 0x00000080,
        )
        if watch < 0:
            raise OSError(ctypes.get_errno(), "inotify_add_watch")
        write_private(
            evidence / "claim-watch-ready.json",
            {
                "schema": "task26-bootstrap-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 / "claim-transition-receipt.json",
                    {
                        "schema": "task26-bootstrap-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 = os.read(descriptor, 65536)
            offset = 0
            ledger_changed = 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
                ledger_changed = ledger_changed or name == b"ledger.json"
            if not ledger_changed:
                continue
            current = store.get(session_id)
            if current.state is BootstrapState.PREPARED:
                continue
            registry = json.loads(
                (profile / "customers" / "registry.json").read_text(encoding="utf-8")
            )
            write_private(
                evidence / "claim-transition-receipt.json",
                {
                    "schema": "task26-bootstrap-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": sha256(store.ledger_path),
                    "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())
