import json
import tempfile
import unittest
from pathlib import Path

from gnos.core.derive import derive_from_manifest, DeriveError
from gnos.core.hash import sha256_hex
from gnos.core.store import ObjectStore
from gnos.manifest.codec import canonical_json_bytes, load_manifest_object
import gnos.chunking.registry as reg
from examples.gnos_example_derived_chunker.chunker import ExampleDerivedChunker


class TestDerive(unittest.TestCase):
    def test_derive_from_manifest(self) -> None:
        with tempfile.TemporaryDirectory() as tmp:
            root = Path(tmp)
            store = ObjectStore(root / ".gnos")
            store.init()

            # Create a doc-manifest via gnos add.
            (root / "a.md").write_text("# A\n", encoding="utf-8")
            import os
            from gnos.cli.main import main

            old_cwd = Path.cwd()
            try:
                os.chdir(root)
                self.assertEqual(main(["init"]), 0)
                self.assertEqual(main(["add", "a.md"]), 0)
            finally:
                os.chdir(old_cwd)

            index = json.loads((root / ".gnos" / "index.json").read_text(encoding="utf-8"))
            doc_id = list(index["documents"].keys())[0]
            manifest_hash = index["documents"][doc_id]["manifests"][0]

            reg._CHUNKERS["example-derived-v1"] = ExampleDerivedChunker()
            reg._SOURCES["example-derived-v1"] = "example"
            try:
                result = derive_from_manifest(
                    manifest_hash,
                    store,
                    chunking="example-derived-v1",
                )
            finally:
                reg._CHUNKERS.pop("example-derived-v1", None)
                reg._SOURCES.pop("example-derived-v1", None)
            self.assertEqual(result.doc_id, doc_id)
            self.assertEqual(result.source_manifest, manifest_hash)

            derived_bytes = store.get(result.derived_manifest)
            self.assertIsNotNone(derived_bytes)
            obj = load_manifest_object(derived_bytes or b"{}")
            self.assertEqual(obj.get("type"), "derived-manifest")
            self.assertEqual(obj.get("doc_id"), doc_id)
            self.assertEqual(obj.get("source_manifest"), manifest_hash)

            # Check derived manifest hash matches canonical JSON.
            expected_hash = sha256_hex(canonical_json_bytes(obj))
            self.assertEqual(expected_hash, result.derived_manifest)

    def test_derive_missing_manifest(self) -> None:
        with tempfile.TemporaryDirectory() as tmp:
            root = Path(tmp)
            store = ObjectStore(root / ".gnos")
            store.init()
            with self.assertRaises(DeriveError):
                derive_from_manifest("0" * 64, store, chunking="identity-v1")


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