"""Tests for the attest command."""

import json
from pathlib import Path
from unittest.mock import patch

import pytest
import yaml
from click.testing import CliRunner

from gjalla_precommit.commands.attest import attest


@pytest.fixture
def git_repo(tmp_path):
    git_dir = tmp_path / ".git"
    git_dir.mkdir()
    gjalla_dir = tmp_path / ".gjalla"
    gjalla_dir.mkdir()
    return tmp_path


class TestAttestCommand:
    """Tests for gjalla attest."""

    def test_creates_attestation_file(self, git_repo, monkeypatch):
        """attest creates .gjalla/.commit-attestation.yaml with correct YAML."""
        monkeypatch.chdir(git_repo)
        the_hash = "a" * 64

        runner = CliRunner()
        with patch("gjalla_precommit.commands.attest.get_staged_diff_hash", return_value=the_hash):
            result = runner.invoke(attest, [
                "--staged-diff-hash", the_hash,
                "--agent", "claude-code",
                "--provider", "anthropic",
                "--model", "claude-opus-4-6",
                "--summary", "Added rate limiting",
                "--timestamp", "2026-02-14T12:00:00Z",
            ])

        assert result.exit_code == 0, result.output
        att_path = git_repo / ".gjalla" / ".commit-attestation.yaml"
        assert att_path.exists()

        content = att_path.read_text()
        assert content.startswith("---\n")
        assert content.endswith("---\n")

        # Parse the YAML
        yaml_content = content.strip().strip("---").strip()
        parsed = yaml.safe_load(yaml_content)
        assert parsed["staged_diff_hash"] == the_hash
        assert parsed["agent"] == "claude-code"
        assert parsed["agent_provider"] == "anthropic"
        assert parsed["agent_model"] == "claude-opus-4-6"
        assert parsed["summary"] == "Added rate limiting"
        assert parsed["timestamp"] == "2026-02-14T12:00:00Z"

    def test_rejects_mismatched_diff_hash(self, git_repo, monkeypatch):
        """attest fails when hash doesn't match staged changes."""
        monkeypatch.chdir(git_repo)

        runner = CliRunner()
        with patch("gjalla_precommit.commands.attest.get_staged_diff_hash", return_value="b" * 64):
            result = runner.invoke(attest, [
                "--staged-diff-hash", "a" * 64,
                "--agent", "claude-code",
                "--provider", "anthropic",
                "--model", "claude-opus-4-6",
                "--summary", "Test",
            ])

        assert result.exit_code == 1
        assert "mismatch" in result.output.lower()

    def test_parses_json_rules(self, git_repo, monkeypatch):
        """attest parses --rules JSON correctly."""
        monkeypatch.chdir(git_repo)
        the_hash = "c" * 64
        rules = json.dumps({"checked": True, "applicable": [{"id": "rule-1", "status": "compliant"}]})

        runner = CliRunner()
        with patch("gjalla_precommit.commands.attest.get_staged_diff_hash", return_value=the_hash):
            result = runner.invoke(attest, [
                "--staged-diff-hash", the_hash,
                "--agent", "claude-code",
                "--provider", "anthropic",
                "--model", "claude-opus-4-6",
                "--summary", "Test",
                "--rules", rules,
                "--timestamp", "2026-02-14T12:00:00Z",
            ])

        assert result.exit_code == 0, result.output
        content = (git_repo / ".gjalla" / ".commit-attestation.yaml").read_text()
        yaml_content = content.strip().strip("---").strip()
        parsed = yaml.safe_load(yaml_content)
        assert parsed["rules"]["checked"] is True
        assert parsed["rules"]["applicable"][0]["id"] == "rule-1"

    def test_parses_json_arch_changes(self, git_repo, monkeypatch):
        """attest parses --arch-changes JSON correctly."""
        monkeypatch.chdir(git_repo)
        the_hash = "d" * 64
        arch = json.dumps({
            "architecture_elements": {"changed": True, "details": ["added new module"]},
            "data_flows": {"changed": False},
        })

        runner = CliRunner()
        with patch("gjalla_precommit.commands.attest.get_staged_diff_hash", return_value=the_hash):
            result = runner.invoke(attest, [
                "--staged-diff-hash", the_hash,
                "--agent", "claude-code",
                "--provider", "anthropic",
                "--model", "claude-opus-4-6",
                "--summary", "Test",
                "--arch-changes", arch,
                "--timestamp", "2026-02-14T12:00:00Z",
            ])

        assert result.exit_code == 0, result.output
        content = (git_repo / ".gjalla" / ".commit-attestation.yaml").read_text()
        yaml_content = content.strip().strip("---").strip()
        parsed = yaml.safe_load(yaml_content)
        assert parsed["architecture_elements"]["changed"] is True
        assert parsed["data_flows"]["changed"] is False

    def test_auto_generates_timestamp(self, git_repo, monkeypatch):
        """attest auto-generates timestamp when not provided."""
        monkeypatch.chdir(git_repo)
        the_hash = "e" * 64

        runner = CliRunner()
        with patch("gjalla_precommit.commands.attest.get_staged_diff_hash", return_value=the_hash):
            result = runner.invoke(attest, [
                "--staged-diff-hash", the_hash,
                "--agent", "claude-code",
                "--provider", "anthropic",
                "--model", "claude-opus-4-6",
                "--summary", "Test",
            ])

        assert result.exit_code == 0, result.output
        content = (git_repo / ".gjalla" / ".commit-attestation.yaml").read_text()
        yaml_content = content.strip().strip("---").strip()
        parsed = yaml.safe_load(yaml_content)
        assert "timestamp" in parsed
        assert parsed["timestamp"].endswith("Z")

    def test_parses_remediated_and_needs_review_rules(self, git_repo, monkeypatch):
        """attest preserves remediated/needs-review statuses and note fields."""
        monkeypatch.chdir(git_repo)
        the_hash = "f0" * 32
        rules = json.dumps({
            "checked": True,
            "applicable": [
                {"id": "rule-1", "status": "compliant"},
                {"id": "rule-2", "status": "not-applicable"},
                {"id": "rule-3", "status": "remediated", "note": "Moved DB query to repository layer."},
                {"id": "rule-4", "status": "needs-review", "note": "Unclear if caching violates this rule."},
            ],
        })

        runner = CliRunner()
        with patch("gjalla_precommit.commands.attest.get_staged_diff_hash", return_value=the_hash):
            result = runner.invoke(attest, [
                "--staged-diff-hash", the_hash,
                "--agent", "claude-code",
                "--provider", "anthropic",
                "--model", "claude-opus-4-6",
                "--summary", "Test",
                "--rules", rules,
                "--timestamp", "2026-02-14T12:00:00Z",
            ])

        assert result.exit_code == 0, result.output
        content = (git_repo / ".gjalla" / ".commit-attestation.yaml").read_text()
        yaml_content = content.strip().strip("---").strip()
        parsed = yaml.safe_load(yaml_content)
        applicable = parsed["rules"]["applicable"]
        assert len(applicable) == 4
        assert applicable[2]["status"] == "remediated"
        assert applicable[2]["note"] == "Moved DB query to repository layer."
        assert applicable[3]["status"] == "needs-review"
        assert applicable[3]["note"] == "Unclear if caching violates this rule."

    def test_invalid_rules_json_fails(self, git_repo, monkeypatch):
        """attest fails on invalid --rules JSON."""
        monkeypatch.chdir(git_repo)
        the_hash = "f" * 64

        runner = CliRunner()
        with patch("gjalla_precommit.commands.attest.get_staged_diff_hash", return_value=the_hash):
            result = runner.invoke(attest, [
                "--staged-diff-hash", the_hash,
                "--agent", "claude-code",
                "--provider", "anthropic",
                "--model", "claude-opus-4-6",
                "--summary", "Test",
                "--rules", "not valid json{",
            ])

        assert result.exit_code == 1
        assert "Invalid" in result.output
