"""Private append-only hash-chain authority for commit observer receipts."""
from __future__ import annotations

import fcntl
import hashlib
import json
import os
import stat
from pathlib import Path
from typing import Any

_ZERO_HASH = "0" * 64


class CommitObservationError(RuntimeError):
    """The subscription or receipt authority is unsafe."""


class CommitReceiptLog:
    def __init__(self, path: Path) -> None:
        self.path = path

    def append(
        self, body: dict[str, Any], *, reject_watcher: str | None = None
    ) -> dict[str, object]:
        descriptor = -1
        try:
            descriptor = os.open(
                self.path,
                os.O_RDWR | os.O_APPEND | os.O_CREAT | os.O_CLOEXEC | os.O_NOFOLLOW,
                0o600,
            )
            metadata = os.fstat(descriptor)
            if (
                not stat.S_ISREG(metadata.st_mode)
                or metadata.st_nlink != 1
                or stat.S_IMODE(metadata.st_mode) != 0o600
            ):
                raise CommitObservationError("receipt_not_private")
            fcntl.flock(descriptor, fcntl.LOCK_EX)
            records = self._read_chain(descriptor)
            if reject_watcher and any(
                row.get("watcher_id") == reject_watcher for row in records
            ):
                raise CommitObservationError("duplicate_watcher_identity")
            predecessor = records[-1]["receipt_hash"] if records else _ZERO_HASH
            unsigned = {**body, "predecessor": predecessor}
            record = {
                **unsigned,
                "receipt_hash": hashlib.sha256(self._canonical(unsigned)).hexdigest(),
            }
            os.write(descriptor, self._canonical(record) + b"\n")
            os.fsync(descriptor)
            return record
        except CommitObservationError:
            raise
        except OSError as exc:
            raise CommitObservationError("receipt_write_failed") from exc
        finally:
            if descriptor >= 0:
                os.close(descriptor)

    def _read_chain(self, descriptor: int) -> list[dict[str, Any]]:
        try:
            raw = os.pread(descriptor, os.fstat(descriptor).st_size, 0)
            records = [json.loads(line) for line in raw.splitlines()]
        except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
            raise CommitObservationError("receipt_chain_corrupt") from exc
        predecessor, sequences = _ZERO_HASH, {}
        for row in records:
            if not isinstance(row, dict) or row.get("predecessor") != predecessor:
                raise CommitObservationError("receipt_chain_corrupt")
            unsigned = {key: value for key, value in row.items() if key != "receipt_hash"}
            if row.get("receipt_hash") != hashlib.sha256(
                self._canonical(unsigned)
            ).hexdigest():
                raise CommitObservationError("receipt_chain_corrupt")
            predecessor = row["receipt_hash"]
            watcher = str(row.get("watcher_id"))
            if row.get("kind") == "subscription_armed":
                sequences[watcher] = row.get("starting_cursor")
            elif row.get("kind") == "commit_observed":
                sequence = row.get("sequence")
                if type(sequence) is not int or sequence <= sequences.get(watcher, -1):
                    raise CommitObservationError("receipt_chain_corrupt")
                sequences[watcher] = sequence
        return records

    @staticmethod
    def _canonical(value: dict[str, Any]) -> bytes:
        return json.dumps(value, sort_keys=True, separators=(",", ":")).encode()
