from __future__ import annotations

import textwrap
from pathlib import Path

import pytest

from leshy.core.config import _load_tests_from_directory, load_config


class TestLoadTestsFromDirectory:
    def test_loads_single_yaml(self, tmp_path: Path) -> None:
        cat_dir = tmp_path / "prompt_injection"
        cat_dir.mkdir()
        (cat_dir / "tests.yaml").write_text(textwrap.dedent("""\
            tests:
              - name: PI Test
                attacks:
                  - name: prompt_injection.direct
        """))

        tests = _load_tests_from_directory(tmp_path)
        assert len(tests) == 1
        assert tests[0].name == "PI Test"
        assert tests[0].attacks[0].name == "prompt_injection.direct"

    def test_loads_multiple_categories(self, tmp_path: Path) -> None:
        for cat in ["prompt_injection", "jailbreak"]:
            d = tmp_path / cat
            d.mkdir()
            (d / "tests.yaml").write_text(textwrap.dedent(f"""\
                tests:
                  - name: {cat} Test
                    attacks:
                      - name: {cat}.direct
            """))

        tests = _load_tests_from_directory(tmp_path)
        assert len(tests) == 2
        names = {t.name for t in tests}
        assert "prompt_injection Test" in names
        assert "jailbreak Test" in names

    def test_skips_files_without_tests_key(self, tmp_path: Path) -> None:
        (tmp_path / "readme.yaml").write_text("description: not a test file\n")
        d = tmp_path / "valid"
        d.mkdir()
        (d / "tests.yaml").write_text(textwrap.dedent("""\
            tests:
              - name: Valid Test
                attacks:
                  - name: prompt_injection.direct
        """))

        tests = _load_tests_from_directory(tmp_path)
        assert len(tests) == 1
        assert tests[0].name == "Valid Test"

    def test_empty_directory(self, tmp_path: Path) -> None:
        tests = _load_tests_from_directory(tmp_path)
        assert tests == []

    def test_nonexistent_directory_raises(self, tmp_path: Path) -> None:
        with pytest.raises(ValueError, match="Tests directory not found"):
            _load_tests_from_directory(tmp_path / "nonexistent")

    def test_multiple_tests_in_one_file(self, tmp_path: Path) -> None:
        (tmp_path / "tests.yaml").write_text(textwrap.dedent("""\
            tests:
              - name: Test A
                attacks:
                  - name: prompt_injection.direct
              - name: Test B
                attacks:
                  - name: jailbreak.roleplay
        """))

        tests = _load_tests_from_directory(tmp_path)
        assert len(tests) == 2


class TestLoadConfigWithTestsDir:
    def test_loads_from_tests_dir(self, tmp_path: Path) -> None:
        # Create tests directory
        tests_dir = tmp_path / "my_tests"
        cat_dir = tests_dir / "pi"
        cat_dir.mkdir(parents=True)
        (cat_dir / "tests.yaml").write_text(textwrap.dedent("""\
            tests:
              - name: Directory Test
                attacks:
                  - name: prompt_injection.direct
        """))

        # Create main config
        config_file = tmp_path / "config.yaml"
        config_file.write_text(textwrap.dedent("""\
            name: Dir Test Suite
            target:
              type: http
              url: https://example.com/chat
              body_template: '{"message": "{{payload}}"}'
            tests_dir: my_tests
        """))

        config = load_config(config_file)
        assert config.name == "Dir Test Suite"
        assert len(config.tests) == 1
        assert config.tests[0].name == "Directory Test"

    def test_merges_inline_and_directory_tests(self, tmp_path: Path) -> None:
        tests_dir = tmp_path / "extra"
        tests_dir.mkdir()
        (tests_dir / "tests.yaml").write_text(textwrap.dedent("""\
            tests:
              - name: From Directory
                attacks:
                  - name: jailbreak.roleplay
        """))

        config_file = tmp_path / "config.yaml"
        config_file.write_text(textwrap.dedent("""\
            name: Merged Suite
            target:
              type: http
              url: https://example.com/chat
              body_template: '{"message": "{{payload}}"}'
            tests_dir: extra
            tests:
              - name: Inline Test
                attacks:
                  - name: prompt_injection.direct
        """))

        config = load_config(config_file)
        assert len(config.tests) == 2
        assert config.tests[0].name == "Inline Test"
        assert config.tests[1].name == "From Directory"

    def test_backward_compat_no_tests_dir(self, tmp_path: Path) -> None:
        config_file = tmp_path / "config.yaml"
        config_file.write_text(textwrap.dedent("""\
            name: Old Style
            target:
              type: http
              url: https://example.com/chat
              body_template: '{"message": "{{payload}}"}'
            tests:
              - name: Inline Only
                attacks:
                  - name: prompt_injection.direct
        """))

        config = load_config(config_file)
        assert len(config.tests) == 1
        assert config.tests[0].name == "Inline Only"
        assert config.tests_dir is None
