"""Tests for new PEFT methods — VeRA + OLoRA (Part D of v0.25.0)."""

import pytest

# ---------------------------------------------------------------------------
# LoraConfig schema fields
# ---------------------------------------------------------------------------

class TestLoraConfigFields:
    def test_use_vera_default_false(self):
        from soup_cli.config.schema import LoraConfig

        cfg = LoraConfig()
        assert cfg.use_vera is False

    def test_use_olora_default_false(self):
        from soup_cli.config.schema import LoraConfig

        cfg = LoraConfig()
        assert cfg.use_olora is False

    def test_use_vera_enabled(self):
        from soup_cli.config.schema import LoraConfig

        cfg = LoraConfig(use_vera=True)
        assert cfg.use_vera is True

    def test_use_olora_enabled(self):
        from soup_cli.config.schema import LoraConfig

        cfg = LoraConfig(use_olora=True)
        assert cfg.use_olora is True


# ---------------------------------------------------------------------------
# Mutual exclusion
# ---------------------------------------------------------------------------

class TestPeftMutualExclusion:
    def test_vera_and_olora_rejected(self):
        from pydantic import ValidationError

        from soup_cli.config.schema import LoraConfig

        with pytest.raises(ValidationError):
            LoraConfig(use_vera=True, use_olora=True)

    def test_vera_and_dora_rejected(self):
        from pydantic import ValidationError

        from soup_cli.config.schema import LoraConfig

        with pytest.raises(ValidationError):
            LoraConfig(use_vera=True, use_dora=True)

    def test_olora_and_dora_rejected(self):
        from pydantic import ValidationError

        from soup_cli.config.schema import LoraConfig

        with pytest.raises(ValidationError):
            LoraConfig(use_olora=True, use_dora=True)


# ---------------------------------------------------------------------------
# Peft builder helper
# ---------------------------------------------------------------------------

class TestPeftBuilder:
    def test_standard_lora_returns_lora_config(self):
        from soup_cli.config.schema import LoraConfig as SchemaLoraConfig
        from soup_cli.utils.peft_builder import build_peft_config

        schema_cfg = SchemaLoraConfig()
        result = build_peft_config(
            schema_cfg,
            target_modules=["q_proj", "v_proj"],
            task_type="CAUSAL_LM",
        )
        # Expect dict with `peft_cls` key + init kwargs
        assert result["peft_cls"] == "LoraConfig"

    def test_olora_adds_init_weights(self):
        from soup_cli.config.schema import LoraConfig as SchemaLoraConfig
        from soup_cli.utils.peft_builder import build_peft_config

        schema_cfg = SchemaLoraConfig(use_olora=True)
        result = build_peft_config(
            schema_cfg,
            target_modules=["q_proj", "v_proj"],
            task_type="CAUSAL_LM",
        )
        assert result["peft_cls"] == "LoraConfig"
        assert result["init_kwargs"].get("init_lora_weights") == "olora"

    def test_vera_returns_vera_config(self):
        from soup_cli.config.schema import LoraConfig as SchemaLoraConfig
        from soup_cli.utils.peft_builder import build_peft_config

        schema_cfg = SchemaLoraConfig(use_vera=True)
        result = build_peft_config(
            schema_cfg,
            target_modules=["q_proj", "v_proj"],
            task_type="CAUSAL_LM",
        )
        assert result["peft_cls"] == "VeraConfig"

    def test_dora_preserved(self):
        from soup_cli.config.schema import LoraConfig as SchemaLoraConfig
        from soup_cli.utils.peft_builder import build_peft_config

        schema_cfg = SchemaLoraConfig(use_dora=True)
        result = build_peft_config(
            schema_cfg,
            target_modules=["q_proj"],
            task_type="CAUSAL_LM",
        )
        assert result["peft_cls"] == "LoraConfig"
        assert result["init_kwargs"].get("use_dora") is True

    def test_target_modules_propagated_lora(self):
        from soup_cli.config.schema import LoraConfig as SchemaLoraConfig
        from soup_cli.utils.peft_builder import build_peft_config

        modules = ["q_proj", "k_proj", "v_proj", "o_proj"]
        result = build_peft_config(
            SchemaLoraConfig(),
            target_modules=modules,
            task_type="CAUSAL_LM",
        )
        assert result["init_kwargs"]["target_modules"] == modules

    def test_target_modules_propagated_vera(self):
        from soup_cli.config.schema import LoraConfig as SchemaLoraConfig
        from soup_cli.utils.peft_builder import build_peft_config

        modules = ["q_proj", "v_proj"]
        result = build_peft_config(
            SchemaLoraConfig(use_vera=True),
            target_modules=modules,
            task_type="CAUSAL_LM",
        )
        assert result["init_kwargs"]["target_modules"] == modules

    def test_task_type_propagated(self):
        from soup_cli.config.schema import LoraConfig as SchemaLoraConfig
        from soup_cli.utils.peft_builder import build_peft_config

        result = build_peft_config(
            SchemaLoraConfig(),
            target_modules=["q_proj"],
            task_type="SEQ_CLS",
        )
        assert result["init_kwargs"]["task_type"] == "SEQ_CLS"


# ---------------------------------------------------------------------------
# Sweep integration
# ---------------------------------------------------------------------------

class TestPeftSweep:
    def test_sweep_accepts_use_vera(self, tmp_path, monkeypatch):
        from soup_cli.commands.sweep import _parse_sweep_params

        params = _parse_sweep_params(["lora.use_vera=true,false"])
        assert "lora.use_vera" in params

    def test_sweep_accepts_use_olora(self):
        from soup_cli.commands.sweep import _parse_sweep_params

        params = _parse_sweep_params(["lora.use_olora=true,false"])
        assert "lora.use_olora" in params


# ---------------------------------------------------------------------------
# End-to-end config loads
# ---------------------------------------------------------------------------

class TestPeftYamlConfig:
    def test_yaml_with_vera(self):
        from soup_cli.config.loader import load_config_from_string

        yaml_str = """
base: meta-llama/Llama-3.1-8B-Instruct
task: sft
data:
  train: ./data/train.jsonl
  format: auto
training:
  epochs: 1
  lora:
    use_vera: true
output: ./output
"""
        cfg = load_config_from_string(yaml_str)
        assert cfg.training.lora.use_vera is True
        assert cfg.training.lora.use_olora is False

    def test_yaml_with_olora(self):
        from soup_cli.config.loader import load_config_from_string

        yaml_str = """
base: meta-llama/Llama-3.1-8B-Instruct
task: sft
data:
  train: ./data/train.jsonl
  format: auto
training:
  epochs: 1
  lora:
    use_olora: true
output: ./output
"""
        cfg = load_config_from_string(yaml_str)
        assert cfg.training.lora.use_olora is True


if __name__ == "__main__":
    pytest.main([__file__, "-v"])
