"""
Tests for the content-type system configuration.

Validates library type defaults, vision prompts, and the
get_library_type_config helper.
"""

from django.test import TestCase

from library.content_types import LIBRARY_TYPE_DEFAULTS, get_library_type_config


class LibraryTypeDefaultsTests(TestCase):
    """Tests for the LIBRARY_TYPE_DEFAULTS registry."""

    EXPECTED_TYPES = {
        "fiction",
        "nonfiction",
        "technical",
        "music",
        "film",
        "art",
        "journal",
        "email",
        "business",
        "finance",
    }

    def test_all_expected_types_present(self):
        for lib_type in self.EXPECTED_TYPES:
            self.assertIn(lib_type, LIBRARY_TYPE_DEFAULTS, f"Missing library type: {lib_type}")

    def test_no_unexpected_types(self):
        for lib_type in LIBRARY_TYPE_DEFAULTS:
            self.assertIn(lib_type, self.EXPECTED_TYPES, f"Unexpected library type: {lib_type}")

    def test_each_type_has_required_keys(self):
        required_keys = {
            "chunking_config",
            "embedding_instruction",
            "reranker_instruction",
            "llm_context_prompt",
            "vision_prompt",
        }
        for lib_type, config in LIBRARY_TYPE_DEFAULTS.items():
            for key in required_keys:
                self.assertIn(
                    key, config,
                    f"Library type '{lib_type}' missing key '{key}'",
                )

    def test_chunking_configs_have_strategy(self):
        for lib_type, config in LIBRARY_TYPE_DEFAULTS.items():
            cc = config["chunking_config"]
            self.assertIn("strategy", cc, f"'{lib_type}' chunking_config missing 'strategy'")
            self.assertIn("chunk_size", cc, f"'{lib_type}' chunking_config missing 'chunk_size'")
            self.assertIn("chunk_overlap", cc, f"'{lib_type}' chunking_config missing 'chunk_overlap'")

    def test_chunk_sizes_are_positive(self):
        for lib_type, config in LIBRARY_TYPE_DEFAULTS.items():
            cc = config["chunking_config"]
            self.assertGreater(cc["chunk_size"], 0, f"'{lib_type}' chunk_size must be positive")
            self.assertGreaterEqual(cc["chunk_overlap"], 0, f"'{lib_type}' chunk_overlap must be >= 0")

    def test_chunk_overlap_less_than_size(self):
        for lib_type, config in LIBRARY_TYPE_DEFAULTS.items():
            cc = config["chunking_config"]
            self.assertLess(
                cc["chunk_overlap"], cc["chunk_size"],
                f"'{lib_type}' chunk_overlap must be less than chunk_size",
            )

    def test_instructions_are_nonempty_strings(self):
        for lib_type, config in LIBRARY_TYPE_DEFAULTS.items():
            for key in ["embedding_instruction", "reranker_instruction", "llm_context_prompt", "vision_prompt"]:
                val = config[key]
                self.assertIsInstance(val, str, f"'{lib_type}'.{key} should be str")
                self.assertTrue(len(val) > 10, f"'{lib_type}'.{key} seems too short")


class VisionPromptTests(TestCase):
    """Tests for vision prompts across library types."""

    def test_fiction_vision_prompt_mentions_characters(self):
        config = get_library_type_config("fiction")
        prompt = config["vision_prompt"].lower()
        self.assertIn("character", prompt)

    def test_technical_vision_prompt_mentions_diagram(self):
        config = get_library_type_config("technical")
        prompt = config["vision_prompt"].lower()
        self.assertIn("diagram", prompt)

    def test_music_vision_prompt_mentions_album(self):
        config = get_library_type_config("music")
        prompt = config["vision_prompt"].lower()
        self.assertIn("album", prompt)

    def test_film_vision_prompt_mentions_still(self):
        config = get_library_type_config("film")
        prompt = config["vision_prompt"].lower()
        self.assertIn("still", prompt)

    def test_art_vision_prompt_mentions_style(self):
        config = get_library_type_config("art")
        prompt = config["vision_prompt"].lower()
        self.assertIn("style", prompt)

    def test_journal_vision_prompt_mentions_date(self):
        config = get_library_type_config("journal")
        prompt = config["vision_prompt"].lower()
        self.assertIn("date", prompt)

    def test_nonfiction_vision_prompt_mentions_historical(self):
        config = get_library_type_config("nonfiction")
        prompt = config["vision_prompt"].lower()
        self.assertIn("historical", prompt)

    def test_business_vision_prompt_mentions_logo_or_chart(self):
        config = get_library_type_config("business")
        prompt = config["vision_prompt"].lower()
        self.assertTrue("logo" in prompt or "chart" in prompt)

    def test_finance_llm_context_forbids_fabrication(self):
        config = get_library_type_config("finance")
        prompt = config["llm_context_prompt"].lower()
        self.assertIn("fabricate", prompt)


class GetLibraryTypeConfigTests(TestCase):
    """Tests for the get_library_type_config helper."""

    def test_returns_dict_for_valid_type(self):
        config = get_library_type_config("fiction")
        self.assertIsInstance(config, dict)

    def test_raises_for_unknown_type(self):
        with self.assertRaises(ValueError) as ctx:
            get_library_type_config("nonexistent")
        self.assertIn("Unknown library type", str(ctx.exception))
        self.assertIn("nonexistent", str(ctx.exception))

    def test_all_types_retrievable(self):
        for lib_type in LIBRARY_TYPE_DEFAULTS:
            config = get_library_type_config(lib_type)
            self.assertIsNotNone(config)


class NonfictionTypeTests(TestCase):
    """Tests specific to the nonfiction library type (Phase 2B addition)."""

    def test_nonfiction_strategy(self):
        config = get_library_type_config("nonfiction")
        self.assertEqual(config["chunking_config"]["strategy"], "section_aware")

    def test_nonfiction_chunk_size(self):
        config = get_library_type_config("nonfiction")
        self.assertEqual(config["chunking_config"]["chunk_size"], 768)

    def test_nonfiction_chunk_overlap(self):
        config = get_library_type_config("nonfiction")
        self.assertEqual(config["chunking_config"]["chunk_overlap"], 96)

    def test_nonfiction_has_vision_prompt(self):
        config = get_library_type_config("nonfiction")
        self.assertTrue(len(config["vision_prompt"]) > 0)

    def test_nonfiction_boundaries(self):
        config = get_library_type_config("nonfiction")
        boundaries = config["chunking_config"]["respect_boundaries"]
        self.assertIn("chapter", boundaries)
        self.assertIn("section", boundaries)
