"""Tests for tacoshop.commands."""

from __future__ import annotations

import os
import textwrap
from pathlib import Path

import pytest

from tacoshop.cli import ParseResult
from tacoshop.commands import cmd_init, cmd_print, cmd_trace
from tacoshop.config import Config, FlagVar, StaticVar, parse_toml, merge_configs


class TestCmdInit:
    def test_creates_file(self, tmp_path, monkeypatch):
        monkeypatch.chdir(tmp_path)
        with pytest.raises(SystemExit) as exc_info:
            cmd_init()
        assert exc_info.value.code == 0
        created = tmp_path / "tacoshop.toml"
        assert created.exists()
        content = created.read_text()
        assert "tacoshop.toml" in content
        assert "flag" in content

    def test_refuses_if_toml_exists(self, tmp_path, monkeypatch, capsys):
        monkeypatch.chdir(tmp_path)
        (tmp_path / "tacoshop.toml").write_text("X = 1\n")
        with pytest.raises(SystemExit) as exc_info:
            cmd_init()
        assert exc_info.value.code == 1
        captured = capsys.readouterr()
        assert "already exists" in captured.err

    def test_refuses_if_dotfile_exists(self, tmp_path, monkeypatch, capsys):
        monkeypatch.chdir(tmp_path)
        (tmp_path / ".tacoshop.toml").write_text("X = 1\n")
        with pytest.raises(SystemExit) as exc_info:
            cmd_init()
        assert exc_info.value.code == 1
        captured = capsys.readouterr()
        assert ".tacoshop.toml already exists" in captured.err

    def test_template_is_valid_toml(self, tmp_path, monkeypatch):
        """The template (all comments) should parse as valid empty TOML."""
        import tomllib

        monkeypatch.chdir(tmp_path)
        with pytest.raises(SystemExit):
            cmd_init()
        content = (tmp_path / "tacoshop.toml").read_text()
        data = tomllib.loads(content)
        assert data == {}


def _make_config_from_files(*toml_contents: str, tmp_path: Path) -> Config:
    """Write multiple TOML files and merge them into a Config with file_configs."""
    configs = []
    for i, content in enumerate(toml_contents):
        p = tmp_path / f"config_{i}.toml"
        p.write_text(textwrap.dedent(content))
        configs.append(parse_toml(p))
    merged = merge_configs(configs) if configs else Config()
    merged.file_configs = configs
    return merged


class TestCmdShowTrace:
    def test_single_static_var(self, tmp_path, capsys):
        config = _make_config_from_files(
            'MY_VAR = 1\n', tmp_path=tmp_path
        )
        result = ParseResult()
        with pytest.raises(SystemExit) as exc_info:
            cmd_trace(config, result)
        assert exc_info.value.code == 0
        err = capsys.readouterr().err
        assert "MY_VAR" in err
        assert "set" in err
        assert "'1'" in err

    def test_static_var_overwritten_across_files(self, tmp_path, capsys):
        config = _make_config_from_files(
            'X = 1\n',
            'X = 2\n',
            tmp_path=tmp_path,
        )
        result = ParseResult()
        with pytest.raises(SystemExit):
            cmd_trace(config, result)
        err = capsys.readouterr().err
        assert "set" in err
        assert "overwritten" in err
        assert "'2'" in err

    def test_static_then_flag_inactive_not_overwritten(self, tmp_path, capsys):
        """A flag var in a later file shouldn't show as overwritten when inactive."""
        config = _make_config_from_files(
            'MY_VAR = 1\n',
            textwrap.dedent("""\
                [MY_VAR]
                flag = "--myvar"
                value = 99
            """),
            tmp_path=tmp_path,
        )
        result = ParseResult()
        with pytest.raises(SystemExit):
            cmd_trace(config, result)
        err = capsys.readouterr().err
        assert "set → '1'" in err
        assert "if --myvar → '99'" in err
        assert "overwritten" not in err

    def test_static_then_flag_active_shows_overwritten(self, tmp_path, capsys):
        """A flag var in a later file shows overwritten when active."""
        config = _make_config_from_files(
            'MY_VAR = 1\n',
            textwrap.dedent("""\
                [MY_VAR]
                flag = "--myvar"
                value = 99
            """),
            tmp_path=tmp_path,
        )
        result = ParseResult(activated_flags={"MY_VAR": "99"})
        with pytest.raises(SystemExit):
            cmd_trace(config, result)
        err = capsys.readouterr().err
        assert "set → '1'" in err
        assert "overwritten → '99' (--myvar)" in err

    def test_static_var_with_env(self, tmp_path, monkeypatch, capsys):
        monkeypatch.setenv("MY_VAR", "from_env")
        config = _make_config_from_files(
            'MY_VAR = "from_config"\n', tmp_path=tmp_path
        )
        result = ParseResult()
        with pytest.raises(SystemExit):
            cmd_trace(config, result)
        err = capsys.readouterr().err
        assert "environment" in err
        assert "'from_env'" in err
        assert "overwritten" in err
        assert "'from_config'" in err

    def test_append_static_trace(self, tmp_path, monkeypatch, capsys):
        monkeypatch.setenv("MY_PATH", "/usr/bin")
        config = _make_config_from_files(
            textwrap.dedent("""\
                [MY_PATH]
                value = "/extra/bin"
                append = true
                separator = ":"
            """),
            textwrap.dedent("""\
                [MY_PATH]
                value = "/more/bin"
                append = true
                separator = ":"
            """),
            tmp_path=tmp_path,
        )
        result = ParseResult()
        with pytest.raises(SystemExit):
            cmd_trace(config, result)
        err = capsys.readouterr().err
        assert "environment" in err
        assert "'/usr/bin'" in err
        assert "appended" in err
        assert "'/usr/bin:/extra/bin'" in err
        assert "'/usr/bin:/extra/bin:/more/bin'" in err

    def test_flag_var_inactive_shows_conditional(self, tmp_path, capsys):
        config = _make_config_from_files(
            textwrap.dedent("""\
                [TRITON]
                flag = "--triton"
                value = 1
            """),
            textwrap.dedent("""\
                [TRITON]
                flag = "--triton"
                value = 2
                description = "Override"
            """),
            tmp_path=tmp_path,
        )
        result = ParseResult()
        with pytest.raises(SystemExit):
            cmd_trace(config, result)
        err = capsys.readouterr().err
        assert "if --triton → '1'" in err
        assert "if --triton → '2'" in err
        # Should NOT contain set/overwritten since flag is inactive
        assert "set →" not in err
        assert "overwritten" not in err

    def test_flag_var_active_set_and_overwritten(self, tmp_path, capsys):
        config = _make_config_from_files(
            textwrap.dedent("""\
                [TRITON]
                flag = "--triton"
                value = 1
            """),
            textwrap.dedent("""\
                [TRITON]
                flag = "--triton"
                value = 2
                description = "Override"
            """),
            tmp_path=tmp_path,
        )
        result = ParseResult(activated_flags={"TRITON": "2"})
        with pytest.raises(SystemExit):
            cmd_trace(config, result)
        err = capsys.readouterr().err
        assert "set → '1' (--triton)" in err
        assert "overwritten → '2' (--triton)" in err

    def test_flag_var_value_required_inactive(self, tmp_path, capsys):
        config = _make_config_from_files(
            textwrap.dedent("""\
                [LOGS]
                flag = "--logs"
            """),
            tmp_path=tmp_path,
        )
        result = ParseResult()
        with pytest.raises(SystemExit):
            cmd_trace(config, result)
        err = capsys.readouterr().err
        assert "defined --logs" in err

    def test_flag_var_value_required_active(self, tmp_path, capsys):
        config = _make_config_from_files(
            textwrap.dedent("""\
                [LOGS]
                flag = "--logs"
            """),
            tmp_path=tmp_path,
        )
        result = ParseResult(activated_flags={"LOGS": "+dynamo"})
        with pytest.raises(SystemExit):
            cmd_trace(config, result)
        err = capsys.readouterr().err
        assert "defined --logs" in err
        assert "--logs: set → '+dynamo'" in err

    def test_flag_var_value_required_active_append(self, tmp_path, monkeypatch, capsys):
        monkeypatch.setenv("LOGS", "+inductor")
        config = _make_config_from_files(
            textwrap.dedent("""\
                [LOGS]
                flag = "--logs"
                append = true
                separator = ","
            """),
            tmp_path=tmp_path,
        )
        result = ParseResult(activated_flags={"LOGS": "+dynamo"})
        with pytest.raises(SystemExit):
            cmd_trace(config, result)
        err = capsys.readouterr().err
        assert "environment" in err
        assert "'+inductor'" in err
        assert "defined --logs" in err
        assert "--logs: appended '+dynamo' → '+inductor,+dynamo'" in err

    def test_flag_var_user_supplied_defined_redefined(self, tmp_path, capsys):
        config = _make_config_from_files(
            textwrap.dedent("""\
                [LOGS]
                flag = "--logs"
            """),
            textwrap.dedent("""\
                [LOGS]
                flag = "--logs"
                append = true
                separator = ","
            """),
            tmp_path=tmp_path,
        )
        result = ParseResult()
        with pytest.raises(SystemExit):
            cmd_trace(config, result)
        err = capsys.readouterr().err
        assert "defined" in err
        assert "redefined" in err

    def test_flag_var_with_env_shown(self, tmp_path, monkeypatch, capsys):
        monkeypatch.setenv("LOGS", "+inductor,")
        config = _make_config_from_files(
            textwrap.dedent("""\
                [LOGS]
                flag = "--logs"
                append = true
                separator = ","
            """),
            tmp_path=tmp_path,
        )
        result = ParseResult()
        with pytest.raises(SystemExit):
            cmd_trace(config, result)
        err = capsys.readouterr().err
        assert "environment" in err
        assert "'+inductor,'" in err

    def test_empty_config(self, capsys):
        config = Config()
        result = ParseResult()
        with pytest.raises(SystemExit) as exc_info:
            cmd_trace(config, result)
        assert exc_info.value.code == 0
        err = capsys.readouterr().err
        assert "No configuration loaded" in err

    def test_loaded_files_shown(self, tmp_path, capsys):
        config = _make_config_from_files(
            'X = 1\n', tmp_path=tmp_path
        )
        result = ParseResult()
        with pytest.raises(SystemExit):
            cmd_trace(config, result)
        err = capsys.readouterr().err
        assert "Loaded config files:" in err
        assert "config_0.toml" in err

    def test_prepend_static_trace(self, tmp_path, monkeypatch, capsys):
        monkeypatch.setenv("MY_PATH", "/usr/bin")
        config = _make_config_from_files(
            textwrap.dedent("""\
                [MY_PATH]
                value = "/first/bin"
                prepend = true
                separator = ":"
            """),
            tmp_path=tmp_path,
        )
        result = ParseResult()
        with pytest.raises(SystemExit):
            cmd_trace(config, result)
        err = capsys.readouterr().err
        assert "prepend" in err
        assert "prepended" in err
        assert "'/first/bin:/usr/bin'" in err

    def test_description_shown(self, tmp_path, capsys):
        config = _make_config_from_files(
            textwrap.dedent("""\
                [MY_VAR]
                value = "x"
                description = "A helpful description"
            """),
            tmp_path=tmp_path,
        )
        result = ParseResult()
        with pytest.raises(SystemExit):
            cmd_trace(config, result)
        err = capsys.readouterr().err
        assert "A helpful description" in err


class TestCmdPrint:
    def test_static_var_only(self, capsys):
        config = Config()
        config.static_vars["MY_VAR"] = StaticVar(name="MY_VAR", value="hello")
        result = ParseResult()
        with pytest.raises(SystemExit) as exc_info:
            cmd_print(["python", "train.py"], config, result)
        assert exc_info.value.code == 0
        out = capsys.readouterr().out
        assert "MY_VAR=hello python train.py" in out

    def test_flag_var(self, capsys):
        config = Config()
        config.flag_vars["TRITON"] = FlagVar(
            name="TRITON", flag="--triton", value="1"
        )
        result = ParseResult(activated_flags={"TRITON": "1"})
        with pytest.raises(SystemExit):
            cmd_print(["python", "train.py"], config, result)
        out = capsys.readouterr().out
        assert "TRITON=1 python train.py" in out

    def test_static_and_flag(self, capsys):
        config = Config()
        config.static_vars["A_VAR"] = StaticVar(name="A_VAR", value="42")
        config.flag_vars["Z_VAR"] = FlagVar(
            name="Z_VAR", flag="--zflag", value="on"
        )
        result = ParseResult(activated_flags={"Z_VAR": "on"})
        with pytest.raises(SystemExit):
            cmd_print(["python", "test.py"], config, result)
        out = capsys.readouterr().out
        # Sorted: A_VAR before Z_VAR
        assert "A_VAR=42 Z_VAR=on python test.py" in out

    def test_values_needing_quotes(self, capsys):
        config = Config()
        config.static_vars["LOGS"] = StaticVar(name="LOGS", value="+dynamo +inductor")
        result = ParseResult()
        with pytest.raises(SystemExit):
            cmd_print(["python", "train.py"], config, result)
        out = capsys.readouterr().out
        assert "LOGS='+dynamo +inductor'" in out

    def test_no_command(self, capsys):
        config = Config()
        config.static_vars["X"] = StaticVar(name="X", value="1")
        result = ParseResult()
        with pytest.raises(SystemExit):
            cmd_print([], config, result)
        out = capsys.readouterr().out.strip()
        assert out == "X=1"

    def test_no_vars_just_command(self, capsys):
        config = Config()
        result = ParseResult()
        with pytest.raises(SystemExit):
            cmd_print(["python", "train.py"], config, result)
        out = capsys.readouterr().out.strip()
        assert out == "python train.py"

    def test_append_var_resolved(self, monkeypatch, capsys):
        monkeypatch.setenv("MY_PATH", "/usr/bin")
        config = Config()
        config.static_vars["MY_PATH"] = StaticVar(
            name="MY_PATH", value="/extra/bin",
            append=True, separator=":"
        )
        result = ParseResult()
        with pytest.raises(SystemExit):
            cmd_print(["echo", "hi"], config, result)
        out = capsys.readouterr().out
        assert "MY_PATH=/usr/bin:/extra/bin" in out
