import tempfile
import unittest
from pathlib import Path

from gnos.cli.main import main
from gnos.core.store import ObjectStore
from gnos.core.hash import canonicalize_bytes, sha256_hex
from gnos.index.local import LocalIndex
from gnos.manifest.codec import load_manifest_bytes


class TestMaterializeCollectionAtomic(unittest.TestCase):
    def test_materialize_collection_failure_is_atomic(self) -> None:
        with tempfile.TemporaryDirectory() as tmp:
            root = Path(tmp)
            old_cwd = Path.cwd()
            try:
                import os

                os.chdir(root)
                main(["init"])
                docs = root / "docs"
                docs.mkdir()
                (docs / "a.md").write_text("# A\n", encoding="utf-8")
                (docs / "b.md").write_text("# B\n", encoding="utf-8")

                exit_code = main(["add", "-C", str(docs)])
                self.assertEqual(exit_code, 0)

                store_dir = root / ".gnos" / "objects"
                collection_id = None
                for obj in store_dir.rglob("*"):
                    if not obj.is_file():
                        continue
                    data = obj.read_bytes()
                    if b"collection-manifest" in data:
                        collection_id = obj.name
                        break
                self.assertIsNotNone(collection_id)

                # Break materialization by removing one chunk blob for b.md.
                store = ObjectStore(root / ".gnos")
                index = LocalIndex(root / ".gnos" / "index.json")
                # Find the doc_id for b.md by hashing the file bytes.
                doc_id_b = sha256_hex(canonicalize_bytes((docs / "b.md").read_bytes()))
                manifest_hash = index.load()["documents"][doc_id_b]["manifests"][0]
                manifest_bytes = store.get(manifest_hash)
                self.assertIsNotNone(manifest_bytes)
                manifest = load_manifest_bytes(manifest_bytes)  # type: ignore[arg-type]
                chunk_hash = manifest.chunks[0]["hash"]
                chunk_path = store._path_for(chunk_hash)
                self.assertTrue(chunk_path.exists())
                chunk_path.unlink()

                out_dir = root / "out"
                exit_code = main(["materialize", "collection", collection_id, "--dir", str(out_dir)])
                self.assertNotEqual(exit_code, 0)
                self.assertFalse(out_dir.exists())
            finally:
                os.chdir(old_cwd)


if __name__ == "__main__":
    unittest.main()
