import json
import tempfile
import unittest
from pathlib import Path

import portrait


class DatasetTests(unittest.TestCase):
    def create_review(self, parent, count=20):
        images = []
        for index in range(count):
            name = f"candidate_{index:02d}.png"
            (parent / name).write_bytes(f"unique fixture {index}".encode())
            images.append({"image": name, "accept": True, "identity_ok": True, "quality_ok": True,
                           "caption": "photo of vprsn01 woman wearing a green shirt"})
        return {"images": images}

    def test_accepts_reviewed_dataset(self):
        with tempfile.TemporaryDirectory() as temp:
            parent = Path(temp)
            self.assertEqual(len(portrait.accepted_images(self.create_review(parent), parent)), 20)

    def test_requires_identity_review(self):
        with tempfile.TemporaryDirectory() as temp:
            parent = Path(temp)
            review = self.create_review(parent)
            review["images"][0]["identity_ok"] = False
            with self.assertRaisesRegex(ValueError, "identity/quality"):
                portrait.accepted_images(review, parent)

    def test_rejects_duplicate_content(self):
        with tempfile.TemporaryDirectory() as temp:
            parent = Path(temp)
            review = self.create_review(parent)
            (parent / "candidate_01.png").write_bytes((parent / "candidate_00.png").read_bytes())
            with self.assertRaisesRegex(ValueError, "duplicate"):
                portrait.accepted_images(review, parent)

    def test_requires_trigger_in_caption(self):
        with tempfile.TemporaryDirectory() as temp:
            parent = Path(temp)
            review = self.create_review(parent)
            review["images"][0]["caption"] = "photo of a woman wearing a green shirt"
            with self.assertRaisesRegex(ValueError, "Caption"):
                portrait.accepted_images(review, parent)

    def test_rejects_unreviewed_small_dataset(self):
        with tempfile.TemporaryDirectory() as temp:
            parent = Path(temp)
            with self.assertRaisesRegex(ValueError, "20-30"):
                portrait.accepted_images(self.create_review(parent, 2), parent)

    def test_cannot_select_outside_dataset(self):
        with tempfile.TemporaryDirectory() as temp:
            parent = Path(temp)
            (parent / "outside.png").write_bytes(b"outside")
            (parent / "images").mkdir()
            with self.assertRaises(ValueError):
                portrait.contained_file(parent / "images", "../outside.png")

    def test_detects_extra_training_sheet_and_modified_captions(self):
        with tempfile.TemporaryDirectory() as temp:
            parent = Path(temp)
            review = self.create_review(parent)
            manifest = []
            for item in review["images"]:
                path = parent / item["image"]
                path.with_suffix(".txt").write_text(item["caption"] + "\n", encoding="utf-8")
                manifest.append({"image": path.name, "sha256": portrait.sha256(path), "caption": item["caption"]})
            portrait.save_json(parent / "manifest.json", manifest)
            self.assertEqual(len(portrait.validate_dataset(parent)), 20)
            extra = parent / "contact-sheet.jpg"
            extra.write_bytes(b"must not become a training sample")
            with self.assertRaisesRegex(ValueError, "differ"):
                portrait.validate_dataset(parent)
            extra.unlink()
            (parent / "candidate_00.txt").write_text("changed caption", encoding="utf-8")
            with self.assertRaisesRegex(ValueError, "changed"):
                portrait.validate_dataset(parent)

    def test_model_revisions_are_immutable(self):
        lock = portrait.read_json(portrait.ROOT / "model-lock.json")
        for key in ["base", "adapter"]:
            self.assertRegex(lock[key]["revision"], r"^[a-f0-9]{40}$")


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