#!/usr/bin/env python3
"""Exercise the v1.4 append-only initialization and rollback boundary in isolation."""

from __future__ import annotations

import hashlib
import json
import shutil
from pathlib import Path

from checkin_cli.weekly_operations import CustomerKey
from checkin_cli.weekly_operations_authority import (
    AuthorityId,
    WeeklyOperationsMigrationRequired,
    begin_authority_initialization,
    discover_legacy_sidecars,
)
from checkin_cli.weekly_operations_parent import acquire_parent_authority
from checkin_cli.weekly_operations_store import WeeklyOperationsStore

ROOT = Path("/home/cube/projects/richard/.worktrees/nutricoach-v140-impl")
EVIDENCE = Path("/home/cube/projects/richard/traning coach/task-12-independent-verification-r7/migration/source")
RUN = EVIDENCE / "migration-rollback-run"
RESULT = EVIDENCE / "migration-rollback-independent.json"


def digest(value: bytes) -> str:
    return hashlib.sha256(value).hexdigest()


def tree(root: Path) -> tuple[tuple[str, bytes], ...]:
    return tuple(sorted((path.relative_to(root).as_posix(), path.read_bytes()) for path in root.rglob("*") if path.is_file()))


def main() -> int:
    if RUN.exists():
        shutil.rmtree(RUN)
    profile = RUN / "profile"
    customer = profile / "customers/customer-001"
    legacy = customer / "nutrition-plans/weekly-operations.jsonl"
    canonical = customer / "events/canonical.jsonl"
    legacy.parent.mkdir(parents=True, mode=0o700)
    canonical.parent.mkdir(parents=True, mode=0o700)
    canonical_bytes = b'{"schema":"v1.3.3","event":"preexisting"}\n'
    legacy_bytes = b'{"legacy":"must-not-be-moved"}\n'
    canonical.write_bytes(canonical_bytes)
    legacy.write_bytes(legacy_bytes)
    before = tree(profile)

    try:
        discover_legacy_sidecars((customer,))
    except WeeklyOperationsMigrationRequired as error:
        dry_run_error = str(error)
    else:
        raise AssertionError("legacy sidecar was not rejected for explicit migration")
    assert tree(profile) == before

    authority_path = profile / "weekly-operations-authority"
    authority_path.mkdir(mode=0o700)
    parent = acquire_parent_authority(authority_path)
    authority = None
    try:
        with begin_authority_initialization(parent, AuthorityId("a" * 64)) as transaction:
            authority = transaction.authority
            transaction.acknowledge_binding()
        assert authority is not None
        assert WeeklyOperationsStore.for_authority(authority, CustomerKey("customer-001")).read() == ()
        assert canonical.read_bytes() == canonical_bytes
        assert legacy.read_bytes() == legacy_bytes
        after_apply = tree(profile)
    finally:
        if authority is not None:
            authority.close()
        parent.close()

    shutil.rmtree(authority_path)
    after_rollback = tree(profile)
    assert after_rollback == before
    report = {
        "schema": "nutricoach-v140-task12-independent-migration-rollback-v1",
        "dry_run": {
            "legacy_migration_required": dry_run_error,
            "tree_unchanged": True,
            "canonical_sha256": digest(canonical_bytes),
            "legacy_sha256": digest(legacy_bytes),
        },
        "apply": {
            "authority_initialized": True,
            "authority_files_added": [name for name, _ in after_apply if name not in {name for name, _ in before}],
            "canonical_unchanged": canonical.read_bytes() == canonical_bytes,
            "legacy_unchanged": legacy.read_bytes() == legacy_bytes,
            "sidecar_rows_backfilled": 0,
        },
        "rollback": {
            "authority_removed": not authority_path.exists(),
            "tree_restored_byte_identically": after_rollback == before,
            "canonical_sha256": digest(canonical.read_bytes()),
            "legacy_sha256": digest(legacy.read_bytes()),
        },
    }
    RESULT.write_text(json.dumps(report, sort_keys=True, separators=(",", ":")) + "\n", encoding="utf-8")
    print("MIGRATION_ROLLBACK_INDEPENDENT_PASS")
    return 0


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