"""Tests for config schema and config writer."""

import json

import tomlkit

from codeflash.setup.config_schema import CodeflashConfig
from codeflash.setup.config_writer import (
    _write_package_json,
    _write_pyproject_toml,
    remove_config,
    write_config,
)
from codeflash.setup.detector import detect_project


class TestCodeflashConfig:
    """Tests for CodeflashConfig Pydantic model."""

    def test_default_values(self):
        """Should have sensible defaults."""
        config = CodeflashConfig(language="python")

        assert config.language == "python"
        assert config.module_root == "."
        assert config.tests_root is None
        assert config.formatter_cmds == []
        assert config.git_remote == "origin"
        assert config.disable_telemetry is False

    def test_all_fields(self):
        """Should accept all fields."""
        config = CodeflashConfig(
            language="javascript",
            module_root="src",
            tests_root="tests",
            test_framework="jest",
            formatter_cmds=["npx prettier --write $file"],
            ignore_paths=["dist", "node_modules"],
            benchmarks_root="benchmarks",
            git_remote="upstream",
            disable_telemetry=True,
        )

        assert config.language == "javascript"
        assert config.module_root == "src"
        assert config.tests_root == "tests"
        assert config.test_framework == "jest"
        assert config.formatter_cmds == ["npx prettier --write $file"]
        assert config.ignore_paths == ["dist", "node_modules"]
        assert config.git_remote == "upstream"
        assert config.disable_telemetry is True

    def test_to_pyproject_dict(self):
        """Should convert to pyproject.toml format with kebab-case."""
        config = CodeflashConfig(
            language="python",
            module_root="codeflash",
            tests_root="tests",
            formatter_cmds=["ruff format $file"],
            ignore_paths=["dist"],
        )

        result = config.to_pyproject_dict()

        assert result["module-root"] == "codeflash"
        assert result["tests-root"] == "tests"
        assert result["formatter-cmds"] == ["ruff format $file"]
        assert result["ignore-paths"] == ["dist"]
        # Should not include default values
        assert "git-remote" not in result
        assert "disable-telemetry" not in result

    def test_to_pyproject_dict_minimal(self):
        """Should only include non-default values."""
        config = CodeflashConfig(language="python", module_root="src")

        result = config.to_pyproject_dict()

        assert "module-root" in result
        # Empty formatter should result in "disabled"
        assert result.get("formatter-cmds") == ["disabled"]

    def test_to_package_json_dict(self):
        """Should convert to package.json format with camelCase."""
        config = CodeflashConfig(
            language="javascript",
            module_root="lib",  # Non-default
            formatter_cmds=["npx prettier --write $file"],
            ignore_paths=["dist"],
            disable_telemetry=True,
        )

        result = config.to_package_json_dict()

        assert result["moduleRoot"] == "lib"
        assert result["formatterCmds"] == ["npx prettier --write $file"]
        assert result["ignorePaths"] == ["dist"]
        assert result["disableTelemetry"] is True
        # Should not include default values
        assert "gitRemote" not in result

    def test_to_package_json_dict_minimal(self):
        """Should be empty when all values are defaults."""
        config = CodeflashConfig(
            language="javascript",
            module_root="src",  # Default for JS
        )

        result = config.to_package_json_dict()

        # src is a default, should not be included
        assert "moduleRoot" not in result

    def test_from_detected_project(self, tmp_path):
        """Should create config from DetectedProject."""
        # Create a simple Python project
        (tmp_path / "pyproject.toml").write_text('[project]\nname = "test"')
        (tmp_path / "test").mkdir()
        (tmp_path / "test" / "__init__.py").write_text("")
        (tmp_path / "tests").mkdir()

        detected = detect_project(tmp_path)
        config = CodeflashConfig.from_detected_project(detected)

        assert config.language == detected.language
        assert config.test_framework == detected.test_runner

    def test_from_pyproject_dict(self):
        """Should create config from pyproject.toml dict."""
        data = {
            "module-root": "src",
            "tests-root": "tests",
            "formatter-cmds": ["black $file"],
            "disable-telemetry": True,
        }

        config = CodeflashConfig.from_pyproject_dict(data)

        assert config.module_root == "src"
        assert config.tests_root == "tests"
        assert config.formatter_cmds == ["black $file"]
        assert config.disable_telemetry is True
        assert config.language == "python"  # Default for pyproject

    def test_from_package_json_dict(self):
        """Should create config from package.json dict."""
        data = {"moduleRoot": "lib", "formatterCmds": ["npx prettier --write $file"], "disableTelemetry": True}

        config = CodeflashConfig.from_package_json_dict(data)

        assert config.module_root == "lib"
        assert config.formatter_cmds == ["npx prettier --write $file"]
        assert config.disable_telemetry is True
        assert config.language == "javascript"  # Default for package.json


class TestWritePyprojectToml:
    """Tests for writing to pyproject.toml."""

    def test_creates_new_pyproject(self, tmp_path):
        """Should create pyproject.toml if it doesn't exist."""
        config = CodeflashConfig(language="python", module_root="src", tests_root="tests")

        success, message = _write_pyproject_toml(tmp_path, config)

        assert success is True
        assert (tmp_path / "pyproject.toml").exists()

        # Verify content
        content = (tmp_path / "pyproject.toml").read_text()
        data = tomlkit.parse(content)
        assert "tool" in data
        assert "codeflash" in data["tool"]
        assert data["tool"]["codeflash"]["module-root"] == "src"

    def test_preserves_existing_content(self, tmp_path):
        """Should preserve existing pyproject.toml content."""
        (tmp_path / "pyproject.toml").write_text(
            '[project]\nname = "myapp"\nversion = "1.0.0"\n\n[tool.ruff]\nline-length = 120'
        )

        config = CodeflashConfig(language="python", module_root="src")

        success, message = _write_pyproject_toml(tmp_path, config)

        assert success is True

        # Verify existing content preserved
        content = (tmp_path / "pyproject.toml").read_text()
        data = tomlkit.parse(content)
        assert data["project"]["name"] == "myapp"
        assert data["tool"]["ruff"]["line-length"] == 120
        assert data["tool"]["codeflash"]["module-root"] == "src"

    def test_updates_existing_codeflash_section(self, tmp_path):
        """Should update existing codeflash section."""
        (tmp_path / "pyproject.toml").write_text('[tool.codeflash]\nmodule-root = "old"\ntests-root = "old_tests"')

        config = CodeflashConfig(language="python", module_root="new", tests_root="new_tests")

        success, message = _write_pyproject_toml(tmp_path, config)

        assert success is True

        content = (tmp_path / "pyproject.toml").read_text()
        data = tomlkit.parse(content)
        assert data["tool"]["codeflash"]["module-root"] == "new"
        assert data["tool"]["codeflash"]["tests-root"] == "new_tests"


class TestWritePackageJson:
    """Tests for writing to package.json."""

    def test_adds_codeflash_section(self, tmp_path):
        """Should add codeflash section to package.json."""
        (tmp_path / "package.json").write_text(json.dumps({"name": "myapp", "version": "1.0.0"}, indent=2))

        config = CodeflashConfig(
            language="javascript", module_root="lib", formatter_cmds=["npx prettier --write $file"]
        )

        success, message = _write_package_json(tmp_path, config)

        assert success is True

        # Verify content
        with (tmp_path / "package.json").open() as f:
            data = json.load(f)
        assert data["name"] == "myapp"  # Preserved
        assert "codeflash" in data
        assert data["codeflash"]["moduleRoot"] == "lib"

    def test_preserves_existing_content(self, tmp_path):
        """Should preserve existing package.json content."""
        (tmp_path / "package.json").write_text(
            json.dumps(
                {"name": "myapp", "dependencies": {"lodash": "^4.17.0"}, "devDependencies": {"jest": "^29.0.0"}},
                indent=2,
            )
        )

        config = CodeflashConfig(language="javascript", module_root="lib")

        success, message = _write_package_json(tmp_path, config)

        assert success is True

        with (tmp_path / "package.json").open() as f:
            data = json.load(f)
        assert data["dependencies"]["lodash"] == "^4.17.0"
        assert data["devDependencies"]["jest"] == "^29.0.0"

    def test_removes_empty_codeflash_section(self, tmp_path):
        """Should remove codeflash section if all defaults."""
        (tmp_path / "package.json").write_text(
            json.dumps({"name": "myapp", "codeflash": {"moduleRoot": "old"}}, indent=2)
        )

        # Config with all defaults - should result in empty dict
        config = CodeflashConfig(
            language="javascript",
            module_root="src",  # Default
        )

        success, message = _write_package_json(tmp_path, config)

        assert success is True

        with (tmp_path / "package.json").open() as f:
            data = json.load(f)
        # Empty codeflash section should be removed
        assert "codeflash" not in data

    def test_fails_if_no_package_json(self, tmp_path):
        """Should fail if package.json doesn't exist."""
        config = CodeflashConfig(language="javascript")

        success, message = _write_package_json(tmp_path, config)

        assert success is False
        assert message == f"No package.json found at {tmp_path}"


class TestWriteConfig:
    """Tests for the unified write_config function."""

    def test_writes_to_pyproject_for_python(self, tmp_path):
        """Should write to pyproject.toml for Python projects."""
        (tmp_path / "pyproject.toml").write_text('[project]\nname = "test"')
        (tmp_path / "src").mkdir()
        (tmp_path / "src" / "__init__.py").write_text("")

        detected = detect_project(tmp_path)
        success, message = write_config(detected)

        assert success is True
        assert message == f"Config saved to {tmp_path / 'pyproject.toml'}"

    def test_writes_to_package_json_for_js(self, tmp_path):
        """Should write to package.json for JavaScript projects."""
        (tmp_path / "package.json").write_text('{"name": "test"}')
        (tmp_path / "src").mkdir()

        detected = detect_project(tmp_path)
        success, message = write_config(detected)

        assert success is True


class TestRemoveConfig:
    """Tests for remove_config function."""

    def test_removes_from_pyproject(self, tmp_path):
        """Should remove codeflash section from pyproject.toml."""
        (tmp_path / "pyproject.toml").write_text('[project]\nname = "test"\n\n[tool.codeflash]\nmodule-root = "src"')

        success, message = remove_config(tmp_path, "python")

        assert success is True

        content = (tmp_path / "pyproject.toml").read_text()
        data = tomlkit.parse(content)
        assert data["project"]["name"] == "test"  # Preserved
        assert "codeflash" not in data.get("tool", {})

    def test_removes_from_package_json(self, tmp_path):
        """Should remove codeflash section from package.json."""
        (tmp_path / "package.json").write_text(
            json.dumps({"name": "test", "codeflash": {"moduleRoot": "src"}}, indent=2)
        )

        success, message = remove_config(tmp_path, "javascript")

        assert success is True

        with (tmp_path / "package.json").open() as f:
            data = json.load(f)
        assert data["name"] == "test"  # Preserved
        assert "codeflash" not in data
