from __future__ import annotations

from pathlib import Path

import pytest

from nutricoach_continuity.contract import canonical_source_frame
from nutricoach_continuity.durable import publish_atomic
from nutricoach_continuity.fdio import (
    SecureIoError,
    complete_prefix,
    open_root,
    read_pinned_absolute,
)
from nutricoach_continuity.preparation import pin_file

from .real_support import root_pin


def test_prefix_completion_reads_descriptor_and_preserves_inode(tmp_path: Path) -> None:
    root = tmp_path / "root"
    root.mkdir(mode=0o700)
    data = root / "data"
    prefix = canonical_source_frame()[:137]
    _ = data.write_bytes(prefix)
    data.chmod(0o600)
    pin = pin_file(data, expected_mode=0o600)
    with open_root(root_pin(root)) as opened:
        complete_prefix(opened, pin, canonical_source_frame())
    assert data.stat().st_ino == pin.inode
    assert data.read_bytes() == canonical_source_frame()


def test_prefix_completion_rejects_mode_and_inode_drift(tmp_path: Path) -> None:
    root = tmp_path / "root"
    root.mkdir(mode=0o700)
    data = root / "data"
    _ = data.write_bytes(b"abc")
    data.chmod(0o600)
    pin = pin_file(data, expected_mode=0o600)
    original = root / "original"
    _ = data.rename(original)
    _ = data.write_bytes(b"abc")
    data.chmod(0o600)
    with open_root(root_pin(root)) as opened, pytest.raises(SecureIoError, match="identity"):
        complete_prefix(opened, pin, canonical_source_frame())
    assert data.read_bytes() == b"abc"


def test_control_publication_rejects_nonprivate_root(tmp_path: Path) -> None:
    root = tmp_path / "control"
    root.mkdir(mode=0o755)
    with pytest.raises(SecureIoError, match="directory identity"):
        publish_atomic(root, "receipt.json", b"{}\n")
    assert tuple(root.iterdir()) == ()


def test_absolute_file_pin_rejects_intermediate_symlink(tmp_path: Path) -> None:
    real = tmp_path / "real"
    real.mkdir(mode=0o700)
    path = real / "bound"
    _ = path.write_bytes(b"bound")
    path.chmod(0o600)
    pin = pin_file(path, expected_mode=0o600)
    alias = tmp_path / "alias"
    alias.symlink_to(real, target_is_directory=True)
    with pytest.raises(OSError):
        _ = read_pinned_absolute(pin.model_copy(update={"path": alias / "bound"}))
