"""Tests for CI/CD Templates Generator."""

from __future__ import annotations

import json
from pathlib import Path

import pytest

from mcpaudit.ci_templates import (
    SUPPORTED_PLATFORMS,
    CITemplateOptions,
    format_ci_template_json,
    generate_ci_template,
    generate_github_actions,
    generate_gitlab_ci,
    generate_pre_commit,
    get_default_output_filename,
)


# --- CITemplateOptions model ---


class TestCITemplateOptions:
    def test_defaults(self):
        opts = CITemplateOptions()
        assert opts.severity_threshold == "high"
        assert opts.min_trust_score == 70
        assert opts.output_format == "text"
        assert opts.scan_paths == ["."]
        assert opts.python_version == "3.11"
        assert opts.mcpaudit_version == "latest"
        assert opts.fail_on_findings is True
        assert opts.enable_sarif_upload is True
        assert opts.enable_supply_chain is False
        assert opts.enable_secrets_scan is False

    def test_custom_options(self):
        opts = CITemplateOptions(
            severity_threshold="critical",
            min_trust_score=90,
            scan_paths=["src/", "lib/"],
        )
        assert opts.severity_threshold == "critical"
        assert opts.min_trust_score == 90
        assert opts.scan_paths == ["src/", "lib/"]

    def test_to_dict(self):
        opts = CITemplateOptions()
        d = opts.to_dict()
        assert "severity_threshold" in d
        assert "min_trust_score" in d
        assert "scan_paths" in d
        assert "python_version" in d

    def test_to_dict_keys(self):
        d = CITemplateOptions().to_dict()
        expected = {
            "severity_threshold", "min_trust_score", "output_format",
            "scan_paths", "python_version", "mcpaudit_version",
            "fail_on_findings", "enable_sarif_upload",
            "enable_supply_chain", "enable_secrets_scan",
            "extra_args",
        }
        assert set(d.keys()) == expected

    def test_extra_args(self):
        opts = CITemplateOptions(extra_args=["--no-deobfuscate"])
        assert "--no-deobfuscate" in opts.extra_args


# --- GitHub Actions template ---


class TestGitHubActions:
    def test_basic_template(self):
        yaml = generate_github_actions()
        assert "name: MCP Security Audit" in yaml
        assert "actions/checkout@v4" in yaml
        assert "actions/setup-python@v5" in yaml
        assert "pip install mcpaudit" in yaml
        assert "--severity high" in yaml
        assert "--min-score 70" in yaml

    def test_custom_severity(self):
        opts = CITemplateOptions(severity_threshold="critical")
        yaml = generate_github_actions(opts)
        assert "--severity critical" in yaml

    def test_custom_min_score(self):
        opts = CITemplateOptions(min_trust_score=85)
        yaml = generate_github_actions(opts)
        assert "--min-score 85" in yaml

    def test_custom_python_version(self):
        opts = CITemplateOptions(python_version="3.12")
        yaml = generate_github_actions(opts)
        assert 'python-version: "3.12"' in yaml

    def test_pinned_version(self):
        opts = CITemplateOptions(mcpaudit_version="1.5.0")
        yaml = generate_github_actions(opts)
        assert "pip install mcpaudit==1.5.0" in yaml

    def test_sarif_upload_enabled(self):
        opts = CITemplateOptions(enable_sarif_upload=True)
        yaml = generate_github_actions(opts)
        assert "Upload SARIF" in yaml
        assert "results.sarif" in yaml

    def test_sarif_upload_disabled(self):
        opts = CITemplateOptions(enable_sarif_upload=False)
        yaml = generate_github_actions(opts)
        assert "Upload SARIF" not in yaml

    def test_supply_chain_enabled(self):
        opts = CITemplateOptions(enable_supply_chain=True)
        yaml = generate_github_actions(opts)
        assert "Supply Chain Scan" in yaml

    def test_supply_chain_disabled(self):
        opts = CITemplateOptions(enable_supply_chain=False)
        yaml = generate_github_actions(opts)
        assert "Supply Chain Scan" not in yaml

    def test_secrets_scan_enabled(self):
        opts = CITemplateOptions(enable_secrets_scan=True)
        yaml = generate_github_actions(opts)
        assert "Secret Scan" in yaml

    def test_secrets_scan_disabled(self):
        opts = CITemplateOptions(enable_secrets_scan=False)
        yaml = generate_github_actions(opts)
        assert "Secret Scan" not in yaml

    def test_custom_scan_paths(self):
        opts = CITemplateOptions(scan_paths=["src/", "lib/"])
        yaml = generate_github_actions(opts)
        assert "src/ lib/" in yaml

    def test_json_format(self):
        opts = CITemplateOptions(output_format="json")
        yaml = generate_github_actions(opts)
        assert "--format json" in yaml

    def test_extra_args(self):
        opts = CITemplateOptions(extra_args=["--no-deobfuscate", "--profile strict"])
        yaml = generate_github_actions(opts)
        assert "--no-deobfuscate" in yaml

    def test_permissions(self):
        yaml = generate_github_actions()
        assert "permissions:" in yaml
        assert "contents: read" in yaml
        assert "security-events: write" in yaml


# --- GitLab CI template ---


class TestGitLabCI:
    def test_basic_template(self):
        yaml = generate_gitlab_ci()
        assert "mcpaudit:" in yaml
        assert "stage: test" in yaml
        assert "pip install" in yaml
        assert "--severity high" in yaml

    def test_custom_severity(self):
        opts = CITemplateOptions(severity_threshold="medium")
        yaml = generate_gitlab_ci(opts)
        assert "--severity medium" in yaml

    def test_pinned_version(self):
        opts = CITemplateOptions(mcpaudit_version="1.5.0")
        yaml = generate_gitlab_ci(opts)
        assert "mcpaudit==1.5.0" in yaml

    def test_allow_failure_on(self):
        opts = CITemplateOptions(fail_on_findings=False)
        yaml = generate_gitlab_ci(opts)
        assert "allow_failure: true" in yaml

    def test_allow_failure_off(self):
        opts = CITemplateOptions(fail_on_findings=True)
        yaml = generate_gitlab_ci(opts)
        assert "allow_failure: false" in yaml

    def test_supply_chain_enabled(self):
        opts = CITemplateOptions(enable_supply_chain=True)
        yaml = generate_gitlab_ci(opts)
        assert "supply-chain" in yaml

    def test_secrets_enabled(self):
        opts = CITemplateOptions(enable_secrets_scan=True)
        yaml = generate_gitlab_ci(opts)
        assert "secrets" in yaml

    def test_python_version_in_image(self):
        opts = CITemplateOptions(python_version="3.12")
        yaml = generate_gitlab_ci(opts)
        assert "python:3.12-slim" in yaml

    def test_sast_artifact(self):
        yaml = generate_gitlab_ci()
        assert "sast:" in yaml

    def test_merge_request_rule(self):
        yaml = generate_gitlab_ci()
        assert "merge_request_event" in yaml

    def test_custom_format(self):
        opts = CITemplateOptions(output_format="sarif")
        yaml = generate_gitlab_ci(opts)
        assert "--format sarif" in yaml


# --- Pre-commit template ---


class TestPreCommit:
    def test_basic_template(self):
        yaml = generate_pre_commit()
        assert "repos:" in yaml
        assert "mcpaudit" in yaml
        assert "--severity high" in yaml
        assert "--min-score 70" in yaml

    def test_custom_options(self):
        opts = CITemplateOptions(
            severity_threshold="critical",
            min_trust_score=90,
        )
        yaml = generate_pre_commit(opts)
        assert "--severity critical" in yaml
        assert "--min-score 90" in yaml

    def test_pinned_version(self):
        opts = CITemplateOptions(mcpaudit_version="1.5.0")
        yaml = generate_pre_commit(opts)
        assert "rev: v1.5.0" in yaml

    def test_latest_version(self):
        yaml = generate_pre_commit()
        assert "rev: latest" in yaml

    def test_file_types(self):
        yaml = generate_pre_commit()
        assert "types_or:" in yaml
        assert "python" in yaml

    def test_pass_filenames(self):
        yaml = generate_pre_commit()
        assert "pass_filenames: true" in yaml

    def test_extra_args_included(self):
        opts = CITemplateOptions(extra_args=["--taxonomy"])
        yaml = generate_pre_commit(opts)
        assert "--taxonomy" in yaml

    def test_custom_format(self):
        opts = CITemplateOptions(output_format="json")
        yaml = generate_pre_commit(opts)
        assert "--format json" in yaml


# --- Dispatch / generate_ci_template ---


class TestGenerateCITemplate:
    def test_github(self):
        output = generate_ci_template("github")
        assert "GitHub" in output or "github" in output.lower()

    def test_gitlab(self):
        output = generate_ci_template("gitlab")
        assert "mcpaudit:" in output

    def test_pre_commit(self):
        output = generate_ci_template("pre-commit")
        assert "repos:" in output

    def test_invalid_platform(self):
        with pytest.raises(ValueError, match="Unknown platform"):
            generate_ci_template("circleci")

    def test_with_options(self):
        opts = CITemplateOptions(severity_threshold="critical")
        output = generate_ci_template("github", opts)
        assert "--severity critical" in output


# --- Output filenames ---


class TestOutputFilenames:
    def test_github_filename(self):
        assert get_default_output_filename("github") == ".github/workflows/mcpaudit.yml"

    def test_gitlab_filename(self):
        assert get_default_output_filename("gitlab") == ".gitlab-ci.yml"

    def test_pre_commit_filename(self):
        assert get_default_output_filename("pre-commit") == ".pre-commit-config.yaml"

    def test_unknown_filename(self):
        name = get_default_output_filename("unknown")
        assert "mcpaudit" in name
        assert ".yml" in name


# --- JSON format ---


class TestFormatJSON:
    def test_valid_json(self):
        output = format_ci_template_json("github")
        data = json.loads(output)
        assert "platform" in data
        assert "content" in data
        assert data["platform"] == "github"

    def test_json_has_filename(self):
        output = format_ci_template_json("gitlab")
        data = json.loads(output)
        assert "filename" in data
        assert data["filename"] == ".gitlab-ci.yml"

    def test_json_has_options(self):
        output = format_ci_template_json("github")
        data = json.loads(output)
        assert "options" in data
        assert "severity_threshold" in data["options"]

    def test_json_with_custom_options(self):
        opts = CITemplateOptions(min_trust_score=95)
        output = format_ci_template_json("github", opts)
        data = json.loads(output)
        assert data["options"]["min_trust_score"] == 95


# --- Supported platforms ---


class TestSupportedPlatforms:
    def test_platforms_tuple(self):
        assert isinstance(SUPPORTED_PLATFORMS, tuple)

    def test_github_supported(self):
        assert "github" in SUPPORTED_PLATFORMS

    def test_gitlab_supported(self):
        assert "gitlab" in SUPPORTED_PLATFORMS

    def test_pre_commit_supported(self):
        assert "pre-commit" in SUPPORTED_PLATFORMS

    def test_all_platforms_generate(self):
        for platform in SUPPORTED_PLATFORMS:
            output = generate_ci_template(platform)
            assert len(output) > 50  # Non-trivial output
