"""Laboratoire local de contrat de fichiers : aucun n8n, Redis, S3, PDF ou LLM.
Deux répertoires représentent des stockages privés. Un troisième est partagé
par convention dans ce programme, sans simuler un stockage distribué.
Les octets sont de simples fixtures JSON, pas des documents à analyser.
"""
from pathlib import Path
from tempfile import TemporaryDirectory
from dataclasses import dataclass, replace
from hashlib import sha256

@dataclass(frozen=True)
class Reference:
    key: str
    size: int
    digest: str
    dossier: str
    revision: int


def read_exact(root, ref):
    try:
        data = (root / ref.key).read_bytes()
    except FileNotFoundError:
        return "ABSENT", None
    if len(data) != ref.size:
        return "TAILLE_INCORRECTE", None
    if sha256(data).hexdigest() != ref.digest:
        return "EMPREINTE_INCORRECTE", None
    return "LISIBLE", data


def reference(key, data, dossier="DOS-A", revision=1):
    return Reference(key, len(data), sha256(data).hexdigest(), dossier, revision)


def purge_allowed(now, expires_at, active_jobs):
    # Politique fictive du dépôt métier, pas mécanisme n8n/S3.
    return now >= expires_at and active_jobs == 0


def main():
    count = 0
    def check(condition):
        nonlocal count
        assert condition
        count += 1
    with TemporaryDirectory(prefix="dawap-5032-") as directory:
        root = Path(directory)
        worker_a, worker_b, shared = [root/x for x in ("worker-a", "worker-b", "shared")]
        for path in (worker_a, worker_b, shared): path.mkdir()
        original = b'{"total":1200}'
        other = b'{"total":9000}'
        revised = b'{"total":1500}'
        check(len(original) == len(other) == len(revised))
        local = reference("facture.json", original)
        (worker_a/local.key).write_bytes(original)
        check(read_exact(worker_a, local) == ("LISIBLE", original))
        check(read_exact(worker_b, local) == ("ABSENT", None))
        print("1. Même chemin, autre worker : ABSENT")
        (worker_b/local.key).write_bytes(other)
        check((worker_b/local.key).name == (worker_a/local.key).name)
        check((worker_b/local.key).stat().st_size == local.size)
        check(read_exact(worker_b, local) == ("EMPREINTE_INCORRECTE", None))
        print("2. Même nom, même taille, autre contenu : EMPREINTE_INCORRECTE")
        ref_a = reference("objet-a-r1.bin", original)
        ref_b = reference("objet-b-r1.bin", other, "DOS-B")
        ref_a2 = reference("objet-a-r2.bin", revised, revision=2)
        for ref, data in ((ref_a,original),(ref_b,other),(ref_a2,revised)):
            with (shared/ref.key).open("xb") as target: target.write(data)
            check(read_exact(shared,ref) == ("LISIBLE",data))
        check(read_exact(shared,ref_a)[1] != read_exact(shared,ref_a2)[1])
        check(ref_a.revision == 1 and ref_a2.revision == 2)
        check(len(list(shared.iterdir())) == 3)
        # Une tentative de remplacement doit échouer, même avec une bonne taille.
        try:
            with (shared/ref_a.key).open("xb") as target: target.write(other)
        except FileExistsError:
            prevented = True
        else:
            prevented = False
        check(prevented)
        check(read_exact(shared,ref_a) == ("LISIBLE",original))
        print("3. Trois références distinctes : A/r1, B/r1, A/r2 conservées")
        wrong_pointer = replace(ref_a,key=ref_b.key)
        check(read_exact(shared,wrong_pointer) == ("EMPREINTE_INCORRECTE",None))
        check(read_exact(shared,replace(ref_a,size=99)) == ("TAILLE_INCORRECTE",None))
        (shared/'tronque.bin').write_bytes(original[:-1])
        check(read_exact(shared,replace(ref_a,key='tronque.bin')) == ("TAILLE_INCORRECTE",None))
        # Fenêtre symbolique en jours. Deux tâches ont encore besoin de A/r1.
        active = 2
        check(not purge_allowed(9,10,0))
        check(not purge_allowed(10,10,active))
        active -= 1
        check(not purge_allowed(12,10,active))
        check(read_exact(shared,ref_a) == ("LISIBLE",original))
        active -= 1
        check(purge_allowed(12,10,active))
        (shared/ref_a.key).unlink()
        check(read_exact(shared,ref_a) == ("ABSENT",None))
        check(read_exact(shared,ref_a2) == ("LISIBLE",revised))
        check(read_exact(shared,ref_b) == ("LISIBLE",other))
        print("4. Expiration seule insuffisante ; purge après fin des deux tâches")
        # Témoins incorrects : existence, taille, échéance sans dépendances.
        check((worker_b/local.key).exists())
        check(read_exact(worker_b,local)[0] != 'LISIBLE')
        check((worker_b/local.key).stat().st_size == local.size)
        check(12 >= 10 and not purge_allowed(12,10,1))
        check(sha256(original).hexdigest() != sha256(other).hexdigest())
        print(f"{count} contrôles ; 3 critères insuffisants réfutés ; aucun service externe")

if __name__ == "__main__": main()
