"""Tests for multi-target build functionality."""

import json
from pathlib import Path

import pytest

from ai_forge.builder import ClaudeBuilder
from ai_forge.models import BuildConfig, BuildTarget


def test_build_project_creates_all_targets(tmp_path: Path) -> None:
    """Test that build_project creates all configured targets."""
    # Setup
    snippets_dir = Path("snippets")
    templates_dir = Path("templates")
    output_dir = tmp_path / "output"

    builder = ClaudeBuilder(snippets_dir, templates_dir)
    config = builder.create_default_config()
    config.output_base_dir = output_dir

    # Execute
    builder.build_project(config)

    # Verify all targets were created
    expected_files = [
        output_dir / "CLAUDE.md",
        output_dir / ".claude" / "settings.json",
    ]

    for file_path in expected_files:
        assert file_path.exists(), f"Expected file {file_path} was not created"
        assert file_path.stat().st_size > 0, f"File {file_path} is empty"


def test_build_project_with_custom_config(tmp_path: Path) -> None:
    """Test build_project with custom configuration."""
    # Setup
    snippets_dir = Path("snippets")
    templates_dir = Path("templates")
    output_dir = tmp_path / "custom_output"

    def custom_postprocess(content: str) -> str:
        return content.upper()

    targets = [
        BuildTarget(
            name="claude_md",
            output_path=Path("custom_claude.md"),
            template_name="CLAUDE.md.j2",
            data_builder="build_document",
            post_process=custom_postprocess,
        ),
        BuildTarget(
            name="settings",
            output_path=Path("config") / "settings.json",
            template_name="settings.json.j2",
            data_builder="build_settings_data",
        ),
    ]

    config = BuildConfig(targets=targets, output_base_dir=output_dir)
    builder = ClaudeBuilder(snippets_dir, templates_dir)

    # Execute
    builder.build_project(config)

    # Verify
    claude_file = output_dir / "custom_claude.md"
    settings_file = output_dir / "config" / "settings.json"

    assert claude_file.exists()
    assert settings_file.exists()

    # Check post-processing was applied
    claude_content = claude_file.read_text()
    assert claude_content.isupper(), (
        "Post-processing should have converted to uppercase"
    )


def test_build_settings_data() -> None:
    """Test settings data builder."""
    builder = ClaudeBuilder(Path("snippets"), Path("templates"))
    data = builder.build_settings_data()

    assert isinstance(data, dict)
    assert "hooks" in data
    # Settings should contain hooks configuration


def test_build_hook_data() -> None:
    """Test hook data builder."""
    builder = ClaudeBuilder(Path("snippets"), Path("templates"))
    data = builder.build_hook_data()

    assert isinstance(data, dict)
    assert "project_name" in data
    assert "python_version" in data
    assert "build_commands" in data
    assert data["project_name"] == "ai-forge"
    assert isinstance(data["build_commands"], list)


def test_create_default_config() -> None:
    """Test default configuration creation."""
    config = ClaudeBuilder.create_default_config()

    assert isinstance(config, BuildConfig)
    assert len(config.targets) >= 2  # At least claude_md and settings
    assert config.output_base_dir == Path("output")

    # Check target names include core targets
    target_names = [target.name for target in config.targets]
    assert "claude_md" in target_names
    assert "settings" in target_names
    # Also check that there are hook and command targets
    hook_targets = [name for name in target_names if name.startswith("hook_")]
    command_targets = [name for name in target_names if name.startswith("command_")]
    assert len(hook_targets) > 0, "Should have hook targets"
    assert len(command_targets) > 0, "Should have command targets"


def test_create_claude_md_only_config() -> None:
    """Test Claude MD only configuration creation."""
    config = ClaudeBuilder.create_claude_md_only_config()

    assert isinstance(config, BuildConfig)
    assert len(config.targets) == 1
    assert config.targets[0].name == "claude_md"
    assert config.targets[0].output_path == Path("CLAUDE.md")


def test_build_target_with_invalid_data_builder(tmp_path: Path) -> None:
    """Test error handling for invalid data builder."""
    builder = ClaudeBuilder(Path("snippets"), Path("templates"))

    target = BuildTarget(
        name="invalid",
        output_path=Path("test.txt"),
        template_name="CLAUDE.md.j2",
        data_builder="non_existent_method",
    )

    with pytest.raises(
        ValueError, match="Data builder 'non_existent_method' does not exist"
    ):
        builder._build_target(target, tmp_path)


def test_rendered_settings_json_is_valid(tmp_path: Path) -> None:
    """Test that rendered settings.json is valid JSON."""
    snippets_dir = Path("snippets")
    templates_dir = Path("templates")
    output_dir = tmp_path / "output"

    builder = ClaudeBuilder(snippets_dir, templates_dir)
    config = ClaudeBuilder.create_default_config()
    config.output_base_dir = output_dir

    # Build only settings target
    settings_target = next(t for t in config.targets if t.name == "settings")
    builder._build_target(settings_target, output_dir)

    # Verify JSON is valid
    settings_file = output_dir / ".claude" / "settings.json"
    assert settings_file.exists()

    with settings_file.open() as f:
        settings_data = json.load(f)

    assert isinstance(settings_data, dict)
    assert "hooks" in settings_data


def test_rendered_hook_scripts_are_executable(tmp_path: Path) -> None:
    """Test that rendered hook scripts have proper Python syntax."""
    snippets_dir = Path("snippets")
    templates_dir = Path("templates")
    output_dir = tmp_path / "output"

    builder = ClaudeBuilder(snippets_dir, templates_dir)
    config = ClaudeBuilder.create_default_config()
    config.output_base_dir = output_dir

    # Build hook targets
    hook_targets = [t for t in config.targets if "hook" in t.name]
    for target in hook_targets:
        builder._build_target(target, output_dir)

    # Check that files start with shebang and are valid Python
    for target in hook_targets:
        hook_file = output_dir / target.output_path
        assert hook_file.exists()

        content = hook_file.read_text()
        # Check that it's a valid script file (Python or bash)
        assert content.startswith("#!/usr/bin/env python3") or content.startswith(
            "#!/bin/bash"
        )

        # For Python files, check for main execution pattern
        if content.startswith("#!/usr/bin/env python3"):
            assert 'if __name__ == "__main__":' in content
        # For bash files, check for main function
        elif content.startswith("#!/bin/bash"):
            assert "main" in content


def test_backward_compatibility_build_and_save(tmp_path: Path) -> None:
    """Test that build_and_save still works for backward compatibility."""
    snippets_dir = Path("snippets")
    templates_dir = Path("templates")
    output_file = tmp_path / "CLAUDE.md"

    builder = ClaudeBuilder(snippets_dir, templates_dir)
    builder.build_and_save(output_file)

    assert output_file.exists()
    assert output_file.stat().st_size > 0
