import json
import tempfile
import unittest
from pathlib import Path

from gnos.cli.main import main
from gnos.core.hash import sha256_hex, canonicalize_bytes
from gnos.core.store import ObjectStore
from gnos.manifest.codec import dump_manifest_bytes
from gnos.manifest.schema import DocManifest


class TestCliBasic(unittest.TestCase):
    def test_init_and_status(self) -> None:
        with tempfile.TemporaryDirectory() as tmp:
            root = Path(tmp)
            old_cwd = Path.cwd()
            try:
                # Run in temp dir
                import os

                os.chdir(root)
                self.assertEqual(main(["status"]), 1)
                self.assertEqual(main(["init"]), 0)
                self.assertEqual(main(["status"]), 0)
            finally:
                os.chdir(old_cwd)

    def test_add_file_and_attest_manifest(self) -> None:
        with tempfile.TemporaryDirectory() as tmp:
            root = Path(tmp)
            old_cwd = Path.cwd()
            try:
                import os

                os.chdir(root)
                self.assertEqual(main(["init"]), 0)
                path = root / "README.md"
                path.write_text("# Title\n\nBody\n", encoding="utf-8")
                self.assertEqual(main(["add", str(path)]), 0)

                # Extract latest manifest by scanning store (single manifest expected)
                store = ObjectStore(root / ".gnos")
                manifests = []
                for obj in store.objects_dir().rglob("*"):
                    if obj.is_file():
                        data = obj.read_bytes()
                        try:
                            manifest = DocManifest.from_dict(json.loads(data.decode("utf-8")))
                        except Exception:
                            continue
                        manifests.append(obj.name)
                        # Attest manifest
                        self.assertEqual(main(["attest", "manifest", obj.name]), 0)

                self.assertTrue(manifests)
            finally:
                os.chdir(old_cwd)

    def test_attest_manifest_fail(self) -> None:
        with tempfile.TemporaryDirectory() as tmp:
            root = Path(tmp)
            old_cwd = Path.cwd()
            try:
                import os

                os.chdir(root)
                self.assertEqual(main(["init"]), 0)

                # Create a manifest but do not store chunks
                doc_bytes = b"hello\n"
                doc_id = sha256_hex(canonicalize_bytes(doc_bytes))
                manifest = DocManifest(
                    type="doc-manifest",
                    version=1,
                    doc_id=doc_id,
                    format="markdown",
                    chunking={"method": "sequential-v1", "params": {"chunk_size": 1024}},
                    chunks=[{"hash": sha256_hex(doc_bytes)}],
                )
                manifest_bytes = dump_manifest_bytes(manifest)
                manifest_hash = sha256_hex(manifest_bytes)

                store = ObjectStore(root / ".gnos")
                store.put(manifest_hash, manifest_bytes)

                # Attest should fail due to missing chunk
                self.assertEqual(main(["attest", "manifest", manifest_hash]), 2)
            finally:
                os.chdir(old_cwd)


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