"""Flat authority-root topology and binding tests."""

from __future__ import annotations

import hashlib
import json
import os
from dataclasses import replace
from datetime import date, datetime
from pathlib import Path
from collections.abc import Callable
from unittest import TestCase

import checkin_cli.weekly_operations_layout as layout
import checkin_cli.weekly_operations_publish as publisher
from checkin_cli.weekly_operations import CanonicalPin, CustomerKey, DayState, WeeklyOperationInput, WeeklyOperationsCorruption, canonical_weekly_row, customer_identity_digest
from checkin_cli.weekly_operations_authority import AuthorityId, open_authority_root
from checkin_cli.weekly_operations_canonical_registry_history import REGISTRATION_SCHEMA, REGISTRY_NAME
from checkin_cli.weekly_operations_layout import DATA_SUFFIX, LAYOUT_DIGEST, LAYOUT_SCHEMA, MARKER_NAME, ROOT_INVENTORY_DIGEST, customer_data_name, customer_lock_name
from checkin_cli.weekly_operations_parent import canonical_authority_marker
from checkin_cli.weekly_operations_store import WeeklyOperationsStore
from tests._weekly_operations_support import initialize_at

ASSERTIONS = TestCase()
CUSTOMER = CustomerKey("client_001")
OTHER = CustomerKey("client_002")


def _operation(customer: CustomerKey = CUSTOMER) -> WeeklyOperationInput:
    return WeeklyOperationInput.for_customer(customer, date(2026, 8, 17), DayState.SUBMITTED, CanonicalPin(1, "a" * 64), datetime.fromisoformat("2026-08-17T20:00:00+09:00"))


def _authority(tmp_path: Path):
    path = tmp_path / "authority"
    path.mkdir(parents=True, mode=0o700)
    return path, initialize_at(path, AuthorityId("1" * 64))


def test_initializer_uses_supplied_root_with_only_fixed_marker(tmp_path: Path) -> None:
    path, authority = _authority(tmp_path)
    binding = authority.binding
    assert {item.name for item in path.iterdir()} == {MARKER_NAME, REGISTRY_NAME}
    assert (path.stat().st_dev, path.stat().st_ino) == (binding.root_device, binding.root_inode)
    assert path.stat().st_nlink == binding.root_links == 2
    assert binding.root_inventory_digest == ROOT_INVENTORY_DIGEST and binding.layout_digest == LAYOUT_DIGEST


def test_marker_layout_is_sealed_to_exact_registration_row_schema(tmp_path: Path) -> None:
    path, authority = _authority(tmp_path)

    def digest(schema: str) -> str:
        fields = (
            LAYOUT_SCHEMA, REGISTRY_NAME, schema,
            "<sha256>.day-status-v1.jsonl", "<sha256>.day-status-v1.lock",
            "regular-0600-owner-link1",
        )
        return hashlib.sha256(
            json.dumps(fields, separators=(",", ":")).encode()
        ).hexdigest()

    expected = digest(REGISTRATION_SCHEMA)
    assert REGISTRATION_SCHEMA == "nutricoach-canonical-authority-registration-v2"
    assert authority.binding.layout_digest == LAYOUT_DIGEST == expected
    marker_path = path / MARKER_NAME
    marker = marker_path.read_bytes()
    assert expected.encode() in marker
    stale_digest = digest("canonical-registration-history-v1")
    stale = marker.replace(expected.encode(), stale_digest.encode())
    assert stale != marker
    assert not canonical_authority_marker(stale)
    _ = marker_path.write_bytes(stale)
    with ASSERTIONS.assertRaises(WeeklyOperationsCorruption):
        _ = open_authority_root(authority.parent, authority.binding)


def test_fixed_bootstrap_registry_is_present_and_fully_validated(tmp_path: Path) -> None:
    path, authority = _authority(tmp_path)
    registry = path / REGISTRY_NAME
    assert registry.is_file() and registry.read_bytes() == b""
    authority.verify()
    _ = registry.write_bytes(b"{}\n")
    with ASSERTIONS.assertRaises(WeeklyOperationsCorruption):
        authority.verify()


def test_unknown_root_inventory_entry_blocks_open_without_deletion(tmp_path: Path) -> None:
    path, authority = _authority(tmp_path)
    injected = path / "unexpected"
    _ = injected.write_bytes(b"preserve")
    injected.chmod(0o600)
    with ASSERTIONS.assertRaises(WeeklyOperationsCorruption):
        _ = open_authority_root(authority.parent, authority.binding)
    assert injected.read_bytes() == b"preserve"


def test_marker_mode_and_hardlink_drift_are_rejected(tmp_path: Path) -> None:
    mode_path, mode_authority = _authority(tmp_path / "mode")
    link_path, link_authority = _authority(tmp_path / "link")
    (mode_path / MARKER_NAME).chmod(0o644)
    os.link(link_path / MARKER_NAME, link_path / "marker-hardlink")
    with ASSERTIONS.assertRaises(WeeklyOperationsCorruption):
        _ = open_authority_root(mode_authority.parent, mode_authority.binding)
    with ASSERTIONS.assertRaises(WeeklyOperationsCorruption):
        _ = open_authority_root(link_authority.parent, link_authority.binding)


def test_byte_identical_marker_replacement_fails_bound_restart(tmp_path: Path) -> None:
    path, authority = _authority(tmp_path)
    marker = path / MARKER_NAME
    payload = marker.read_bytes()
    _ = marker.replace(path / "old-marker")
    _ = marker.write_bytes(payload)
    marker.chmod(0o600)
    with ASSERTIONS.assertRaises(WeeklyOperationsCorruption):
        _ = open_authority_root(authority.parent, authority.binding)


def test_flat_namespace_rejects_directory_symlink_fifo_and_unknown_suffix(tmp_path: Path) -> None:
    def directory(child: Path) -> None:
        child.mkdir(mode=0o700)

    def symlink(child: Path) -> None:
        child.symlink_to("outside")

    def fifo(child: Path) -> None:
        os.mkfifo(child, 0o600)

    def unknown(child: Path) -> None:
        _ = child.write_bytes(b"unknown")

    constructors: tuple[Callable[[Path], None], ...] = (directory, symlink, fifo, unknown)
    names = (
        customer_data_name(customer_identity_digest(CUSTOMER)),
        customer_lock_name(customer_identity_digest(CUSTOMER)),
        customer_data_name(customer_identity_digest(CUSTOMER)),
        "a" * 64 + ".unknown",
    )
    for index, (construct, name) in enumerate(zip(constructors, names, strict=True)):
        path, authority = _authority(tmp_path / str(index))
        child = path / name
        construct(child)
        if child.is_file() and not child.is_symlink():
            child.chmod(0o600)
        with ASSERTIONS.assertRaises(WeeklyOperationsCorruption):
            _ = open_authority_root(authority.parent, authority.binding)
        assert child.exists() or child.is_symlink()


def test_two_customers_keep_root_nlink_flat_and_restart_binding(tmp_path: Path) -> None:
    path, authority = _authority(tmp_path)
    binding = authority.binding
    links_before = path.stat().st_nlink
    _ = WeeklyOperationsStore.for_authority(authority, CUSTOMER).append(_operation())
    _ = WeeklyOperationsStore.for_authority(authority, OTHER).append(_operation(OTHER))
    names = {item.name for item in path.iterdir()}
    authority.close()
    reopened = open_authority_root(authority.parent, binding)
    assert path.stat().st_nlink == links_before == binding.root_links == 2
    assert names == {
        MARKER_NAME, REGISTRY_NAME,
        customer_data_name(customer_identity_digest(CUSTOMER)), customer_lock_name(customer_identity_digest(CUSTOMER)),
        customer_data_name(customer_identity_digest(OTHER)), customer_lock_name(customer_identity_digest(OTHER)),
    }
    assert len(WeeklyOperationsStore.for_authority(reopened, CUSTOMER).read()) == 1


def test_binding_inventory_layout_and_root_links_are_immutable(tmp_path: Path) -> None:
    _, authority = _authority(tmp_path)
    for modified in (
        replace(authority.binding, root_inventory_digest="0" * 64),
        replace(authority.binding, layout_digest="0" * 64),
        replace(authority.binding, root_links=3),
    ):
        with ASSERTIONS.assertRaises(WeeklyOperationsCorruption):
            _ = open_authority_root(authority.parent, modified)


def test_subdirectory_injection_changes_root_nlink_and_blocks_use(tmp_path: Path) -> None:
    path, authority = _authority(tmp_path)
    injected = path / "injected-directory"
    injected.mkdir(mode=0o700)
    with ASSERTIONS.assertRaises(WeeklyOperationsCorruption):
        _ = WeeklyOperationsStore.for_authority(authority, CUSTOMER).read()
    assert injected.is_dir()


def test_filename_customer_digest_mismatch_blocks_global_history(tmp_path: Path) -> None:
    path, authority = _authority(tmp_path)
    result = WeeklyOperationsStore.for_authority(authority, CUSTOMER).append(_operation())
    wrong = path / customer_data_name(customer_identity_digest(OTHER))
    _ = wrong.write_bytes(canonical_weekly_row(result.row, include_digest=True) + b"\n")
    wrong.chmod(0o600)
    with ASSERTIONS.assertRaises(WeeklyOperationsCorruption):
        _ = WeeklyOperationsStore.for_authority(authority, OTHER).read()


def test_removed_directory_stage_symbols_are_structurally_absent() -> None:
    assert not hasattr(layout, "CUSTOMERS_NAME")
    assert not hasattr(publisher, "_cleanup_stage")
    assert not hasattr(publisher, "publish_absent_destination")
    assert "weekly-operations-init" not in Path(publisher.__file__).read_text(encoding="utf-8")
    assert "rmdir" not in Path(publisher.__file__).read_text(encoding="utf-8")
    assert DATA_SUFFIX in customer_data_name(customer_identity_digest(CUSTOMER))
