"""Tests for workflow YAML linting using actionlint.

Validates that generated workflows are syntactically valid and follow best practices.
Requires actionlint to be installed: https://github.com/rhysd/actionlint

Note: Uses subprocess to run actionlint - this is safe as we only run on
test-generated files or files in our own repo.
"""
# ruff: noqa: S603, S607  # subprocess calls are safe for test code

from __future__ import annotations

import shutil
import subprocess
from pathlib import Path

import pytest

from cihub.cli import main


def actionlint_available() -> bool:
    """Check if actionlint is available."""
    return shutil.which("actionlint") is not None


@pytest.mark.skipif(not actionlint_available(), reason="actionlint not installed")
class TestGeneratedWorkflowLinting:
    """Test that generated workflows pass actionlint validation."""

    def test_init_workflow_valid(self, tmp_path: Path) -> None:
        """Workflow generated by init should be valid."""
        result = main(
            [
                "init",
                "--repo",
                str(tmp_path),
                "--language",
                "python",
                "--owner",
                "test",
                "--name",
                "repo",
                "--branch",
                "main",
                "--apply",
            ]
        )
        assert result == 0

        workflow = tmp_path / ".github" / "workflows" / "hub-ci.yml"
        assert workflow.exists(), "Workflow file should be created"

        # Run actionlint
        proc = subprocess.run(
            ["actionlint", str(workflow)],
            capture_output=True,
            text=True,
        )
        assert proc.returncode == 0, f"actionlint failed:\n{proc.stdout}\n{proc.stderr}"

    def test_init_java_workflow_valid(self, tmp_path: Path) -> None:
        """Java workflow generated by init should be valid."""
        result = main(
            [
                "init",
                "--repo",
                str(tmp_path),
                "--language",
                "java",
                "--owner",
                "test",
                "--name",
                "repo",
                "--branch",
                "main",
                "--apply",
            ]
        )
        assert result == 0

        workflow = tmp_path / ".github" / "workflows" / "hub-ci.yml"
        assert workflow.exists()

        proc = subprocess.run(
            ["actionlint", str(workflow)],
            capture_output=True,
            text=True,
        )
        assert proc.returncode == 0, f"actionlint failed:\n{proc.stdout}\n{proc.stderr}"

    def test_update_workflow_valid(self, tmp_path: Path) -> None:
        """Workflow updated by update command should be valid."""
        # First init
        main(
            [
                "init",
                "--repo",
                str(tmp_path),
                "--language",
                "python",
                "--owner",
                "test",
                "--name",
                "repo",
                "--branch",
                "main",
                "--apply",
            ]
        )

        # Then update
        result = main(
            [
                "update",
                "--repo",
                str(tmp_path),
                "--language",
                "python",
                "--owner",
                "test",
                "--name",
                "repo",
                "--branch",
                "main",
                "--apply",
                "--force",
            ]
        )
        assert result == 0

        workflow = tmp_path / ".github" / "workflows" / "hub-ci.yml"
        proc = subprocess.run(
            ["actionlint", str(workflow)],
            capture_output=True,
            text=True,
        )
        assert proc.returncode == 0, f"actionlint failed:\n{proc.stdout}\n{proc.stderr}"


@pytest.mark.skipif(not actionlint_available(), reason="actionlint not installed")
class TestExistingWorkflowLinting:
    """Test that existing hub workflows are valid."""

    def test_hub_ci_workflow_valid(self) -> None:
        """Hub's own hub-ci.yml workflow should be valid."""
        workflow = Path(".github/workflows/hub-ci.yml")
        if not workflow.exists():
            pytest.skip("hub-ci.yml not found in repo")

        proc = subprocess.run(
            ["actionlint", str(workflow)],
            capture_output=True,
            text=True,
        )
        # Allow some errors for template expressions
        if proc.returncode != 0:
            # Check if errors are just about matrix/template expressions
            if "matrix" not in proc.stderr and "expression" not in proc.stderr:
                pytest.fail(f"actionlint failed:\n{proc.stdout}\n{proc.stderr}")

    def test_hub_run_all_workflow_valid(self) -> None:
        """Hub's hub-run-all.yml workflow should be valid."""
        workflow = Path(".github/workflows/hub-run-all.yml")
        if not workflow.exists():
            pytest.skip("hub-run-all.yml not found in repo")

        proc = subprocess.run(
            ["actionlint", str(workflow)],
            capture_output=True,
            text=True,
        )
        # Check if it passes or has expected template errors
        if proc.returncode != 0 and "matrix" not in proc.stderr:
            pytest.fail(f"actionlint failed:\n{proc.stdout}\n{proc.stderr}")

    def test_all_workflows_lint(self) -> None:
        """All workflows in .github/workflows should be valid."""
        workflows_dir = Path(".github/workflows")
        if not workflows_dir.exists():
            pytest.skip("No .github/workflows directory")

        workflows = list(workflows_dir.glob("*.yml"))
        if not workflows:
            pytest.skip("No workflow files found")

        errors = []
        for workflow in workflows:
            proc = subprocess.run(
                ["actionlint", str(workflow)],
                capture_output=True,
                text=True,
            )
            if proc.returncode != 0:
                # Skip known template expression issues
                if "matrix" not in proc.stderr and "expression" not in proc.stderr:
                    errors.append(f"{workflow.name}: {proc.stderr}")

        if errors:
            pytest.fail("Workflow lint errors:\n" + "\n".join(errors))


class TestWorkflowYamlParsing:
    """Test workflow YAML structure without actionlint."""

    def test_generated_workflow_parseable(self, tmp_path: Path) -> None:
        """Generated workflow should be valid YAML."""
        import yaml

        result = main(
            [
                "init",
                "--repo",
                str(tmp_path),
                "--language",
                "python",
                "--owner",
                "test",
                "--name",
                "repo",
                "--branch",
                "main",
                "--apply",
            ]
        )
        assert result == 0

        workflow = tmp_path / ".github" / "workflows" / "hub-ci.yml"
        content = workflow.read_text()

        # Should parse without error
        data = yaml.safe_load(content)
        assert data is not None

        # Should have required keys
        assert "name" in data
        # Note: 'on' is parsed as True (boolean) by yaml.safe_load
        assert True in data or "on" in data
        assert "jobs" in data

    def test_generated_workflow_has_permissions(self, tmp_path: Path) -> None:
        """Generated workflow should have security permissions."""
        import yaml

        main(
            [
                "init",
                "--repo",
                str(tmp_path),
                "--language",
                "python",
                "--owner",
                "test",
                "--name",
                "repo",
                "--branch",
                "main",
                "--apply",
            ]
        )

        workflow = tmp_path / ".github" / "workflows" / "hub-ci.yml"
        data = yaml.safe_load(workflow.read_text())

        # Should have permissions block for security
        assert "permissions" in data, "Workflow should have permissions block"

    def test_generated_workflow_has_correlation_id(self, tmp_path: Path) -> None:
        """Generated workflow should pass correlation ID for hub tracking."""
        main(
            [
                "init",
                "--repo",
                str(tmp_path),
                "--language",
                "python",
                "--owner",
                "test",
                "--name",
                "repo",
                "--branch",
                "main",
                "--apply",
            ]
        )

        workflow = tmp_path / ".github" / "workflows" / "hub-ci.yml"
        content = workflow.read_text()

        # Should reference correlation ID somewhere
        assert "correlation" in content.lower() or "hub_correlation_id" in content
