#!/usr/bin/env python3
"""Local document-reachability experiment. No n8n, network, S3 or ERP calls."""
from dataclasses import dataclass, replace
from hashlib import sha256
from pathlib import Path, PurePosixPath
from tempfile import TemporaryDirectory


@dataclass(frozen=True)
class Document:
    source: str
    business_id: str
    relative_path: str
    size: int
    digest: str


def inspect(root: Path, document: Document, expected_source: str) -> str:
    """Compare bytes against a trusted, separately retained fixture manifest."""
    if document.source != expected_source:
        return "wrong_source"
    if not document.business_id.strip():
        return "missing_business_id"
    path = PurePosixPath(document.relative_path)
    if not document.relative_path or path.is_absolute() or ".." in path.parts or "\\" in document.relative_path:
        return "invalid_reference"
    candidate = root.joinpath(*path.parts)
    try:
        candidate.resolve().relative_to(root.resolve())
    except ValueError:
        return "outside_root"
    if not candidate.is_file():
        return "missing_file"
    data = candidate.read_bytes()
    if len(data) != document.size:
        return "size_mismatch"
    if sha256(data).hexdigest() != document.digest:
        return "digest_mismatch"
    return "readable_exact_bytes"


def evidence_key(document: Document) -> str:
    """Local evidence identity, not an n8n token or an ERP idempotency key."""
    return sha256((document.source + "\0" + document.business_id + "\0" + document.digest).encode()).hexdigest()


def run() -> int:
    count = 0
    def check(condition: bool, name: str) -> None:
        nonlocal count
        if not condition:
            raise AssertionError(name)
        count += 1

    payload = b"FICTITIOUS-INVOICE-42\nTOTAL=123.45\n"
    doc = Document("erp-fr-fixture", "invoice-42", "documents/invoice-42.bin", len(payload), sha256(payload).hexdigest())
    with TemporaryDirectory(prefix="dawap-binary-fixture-") as temporary:
        base = Path(temporary)
        main = base / "main"
        workers = [base / "worker-a", base / "worker-b"]
        for root in [main, *workers]:
            (root / "documents").mkdir(parents=True)
        (main / doc.relative_path).write_bytes(payload)
        check(inspect(main, doc, doc.source) == "readable_exact_bytes", "main can read fixture")
        check(inspect(workers[0], doc, doc.source) == "missing_file", "worker A has no bytes")
        check(inspect(workers[1], doc, doc.source) == "missing_file", "worker B has no bytes")
        (workers[0] / doc.relative_path).write_bytes(payload)
        check(inspect(workers[0], doc, doc.source) == "readable_exact_bytes", "A now has identical bytes")
        check(inspect(workers[1], doc, doc.source) == "missing_file", "A success says nothing about B")
        (workers[1] / doc.relative_path).write_bytes(b"X" * len(payload))
        check(inspect(workers[1], doc, doc.source) == "digest_mismatch", "same name and length are insufficient")
        (workers[1] / doc.relative_path).write_bytes(payload[:-1])
        check(inspect(workers[1], doc, doc.source) == "size_mismatch", "truncated file rejected")
        (workers[1] / doc.relative_path).write_bytes(payload)
        check(all(inspect(w, doc, doc.source) == "readable_exact_bytes" for w in workers), "both workers read expected bytes")
        check(inspect(workers[0], doc, "erp-be-fixture") == "wrong_source", "source context matters")
        check(inspect(main, replace(doc, business_id=" "), doc.source) == "missing_business_id", "blank business identity")
        check(inspect(main, replace(doc, relative_path="../secret"), doc.source) == "invalid_reference", "parent traversal")
        check(inspect(main, replace(doc, relative_path="/documents/invoice-42.bin"), doc.source) == "invalid_reference", "absolute path")
        check(inspect(main, replace(doc, relative_path="documents\\invoice-42.bin"), doc.source) == "invalid_reference", "ambiguous separator")
        check(inspect(main, replace(doc, relative_path=""), doc.source) == "invalid_reference", "empty reference")
        check(inspect(main, replace(doc, relative_path="documents"), doc.source) == "missing_file", "directory is not file")
        check(inspect(main, replace(doc, relative_path="documents/absent.bin"), doc.source) == "missing_file", "missing filename")
        check(inspect(main, replace(doc, size=len(payload)+1), doc.source) == "size_mismatch", "manifest size checked")
        check(inspect(main, replace(doc, digest="0"*64), doc.source) == "digest_mismatch", "manifest digest checked")
        outside = base / "outside.bin"
        outside.write_bytes(payload)
        link = main / "documents/link.bin"
        link.symlink_to(outside)
        check(inspect(main, replace(doc, relative_path="documents/link.bin"), doc.source) == "outside_root", "symlink outside fixture")
        check(evidence_key(doc) == evidence_key(doc), "evidence identity deterministic")
        check(evidence_key(doc) != evidence_key(replace(doc, source="erp-be-fixture")), "source namespace separates evidence")
        check(evidence_key(doc) != evidence_key(replace(doc, business_id="invoice-43")), "business document separates evidence")
        check(evidence_key(doc) != evidence_key(replace(doc, digest="0"*64)), "revision separates evidence")
        check(evidence_key(doc) == evidence_key(replace(doc, relative_path="archive/42.bin")), "path relocation is not document revision")
        (workers[1] / doc.relative_path).unlink()
        check(inspect(workers[1], doc, doc.source) == "missing_file", "later deletion changes reachability")
        check(inspect(workers[0], doc, doc.source) == "readable_exact_bytes", "A still works after B loses file")
        (workers[1] / doc.relative_path).write_bytes(payload)
        check(inspect(workers[1], doc, doc.source) == "readable_exact_bytes", "fixture restoration verified")
        check(len({evidence_key(doc), evidence_key(replace(doc, source="erp-be-fixture"))}) == 2, "same invoice id in two sources stays distinct")
    print(f"{count} local checks passed. Native n8n executions: 0; network calls: 0; ERP writes: 0.")
    return count


if __name__ == "__main__":
    run()
