"""Step definitions for CI workflow validation feature."""

from pathlib import Path

import yaml
from behave import given, then, when


@given('the CI workflow file at "{path}"')
def step_given_ci_workflow_file(context, path):
    """Store the CI workflow file path."""
    context.ci_workflow_path = Path(path)


@then("the CI workflow file should exist")
def step_then_ci_workflow_exists(context):
    """Verify the CI workflow file exists."""
    if not context.ci_workflow_path.exists():
        raise AssertionError(
            f"CI workflow file not found at {context.ci_workflow_path}"
        )


@when("I parse the CI workflow YAML")
def step_when_parse_ci_workflow(context):
    """Parse the CI workflow YAML file."""
    workflow_path = context.ci_workflow_path
    if not workflow_path.exists():
        raise FileNotFoundError(f"CI workflow file not found at {workflow_path}")

    with workflow_path.open("r") as f:
        context.ci_workflow = yaml.safe_load(f)


@then('the workflow should have a job named "{job_name}"')
def step_then_workflow_has_job(context, job_name):
    """Verify the workflow has a specific job."""
    jobs = context.ci_workflow.get("jobs", {})
    if job_name not in jobs:
        raise AssertionError(
            f"Job '{job_name}' not found in workflow. "
            f"Available jobs: {list(jobs.keys())}"
        )


@then('the job "{job_name}" should run "{command}"')
def step_then_job_runs_command(context, job_name, command):
    """Verify a job runs a specific command in one of its steps."""
    jobs = context.ci_workflow.get("jobs", {})
    job = jobs.get(job_name)
    if job is None:
        raise AssertionError(f"Job '{job_name}' not found in workflow")

    steps = job.get("steps", [])
    found = False
    for step in steps:
        run_cmd = step.get("run", "")
        if command in run_cmd:
            found = True
            break

    if not found:
        step_runs = [s.get("run", "(no run)") for s in steps if "run" in s]
        raise AssertionError(
            f"Command '{command}' not found in job '{job_name}' steps. "
            f"Step runs: {step_runs}"
        )


@then('the workflow env should set "{key}" to "{value}"')
def step_then_workflow_env_set(context, key, value):
    """Verify a workflow-level environment variable is set."""
    env = context.ci_workflow.get("env", {})
    actual = env.get(key)
    if actual != value:
        raise AssertionError(f"Workflow env '{key}' expected '{value}', got '{actual}'")


@then('the job "{job_name}" should depend on "{dependency}"')
def step_then_job_depends_on(context, job_name, dependency):
    """Verify a job has a specific dependency."""
    jobs = context.ci_workflow.get("jobs", {})
    job = jobs.get(job_name)
    if job is None:
        raise AssertionError(f"Job '{job_name}' not found in workflow")

    needs = job.get("needs", [])
    if isinstance(needs, str):
        needs = [needs]

    if dependency not in needs:
        raise AssertionError(
            f"Job '{job_name}' does not depend on '{dependency}'. Dependencies: {needs}"
        )


@then("the workflow should reference these nox sessions:")
def step_then_workflow_references_nox_sessions(context):
    """Verify all required nox sessions are referenced in the workflow."""
    jobs = context.ci_workflow.get("jobs", {})

    # Collect all run commands across all jobs
    all_run_commands = ""
    for job_data in jobs.values():
        for step in job_data.get("steps", []):
            run_cmd = step.get("run", "")
            all_run_commands += run_cmd + "\n"

    for row in context.table:
        session_name = row["session"]
        nox_call = f"nox -s {session_name}"
        if nox_call not in all_run_commands:
            raise AssertionError(
                f"Nox session '{session_name}' (as '{nox_call}') "
                f"not found in any CI workflow step"
            )


@then("the workflow YAML should be valid")
def step_then_workflow_yaml_valid(context):
    """Verify the parsed YAML is a valid workflow (has jobs key)."""
    if context.ci_workflow is None:
        raise AssertionError("Workflow YAML parsed as None (empty file)")
    if "jobs" not in context.ci_workflow:
        raise AssertionError("Workflow YAML missing 'jobs' key")
    if not isinstance(context.ci_workflow["jobs"], dict):
        raise AssertionError("Workflow 'jobs' is not a mapping")


@then('the workflow should trigger on push tags matching "{pattern}"')
def step_then_workflow_triggers_on_tags(context, pattern):
    """Verify the workflow triggers on push tags matching a pattern."""
    on_config = context.ci_workflow.get("on", context.ci_workflow.get(True, {}))
    push_config = on_config.get("push", {})
    tags = push_config.get("tags", [])
    if pattern not in tags:
        raise AssertionError(f"Tag pattern '{pattern}' not found in push tags: {tags}")


@then("the coverage job should enforce a 97% threshold")
def step_then_coverage_threshold_97(context):
    """Verify the coverage job references the 97% threshold."""
    jobs = context.ci_workflow.get("jobs", {})
    coverage_job = jobs.get("coverage")
    if coverage_job is None:
        raise AssertionError("Coverage job not found in workflow")

    steps = coverage_job.get("steps", [])
    all_run_commands = "\n".join(step.get("run", "") for step in steps if "run" in step)
    if "97" not in all_run_commands:
        raise AssertionError(
            "Coverage job does not reference 97% threshold in any step"
        )


@then('the workflow should trigger on push to "{branch}"')
def step_then_workflow_triggers_on_push(context, branch):
    """Verify the workflow triggers on push to a specific branch."""
    on_config = context.ci_workflow.get("on", context.ci_workflow.get(True, {}))
    push_config = on_config.get("push", {})
    branches = push_config.get("branches", [])
    if branch not in branches:
        raise AssertionError(
            f"Branch '{branch}' not found in push branches: {branches}"
        )


@then('the workflow should trigger on pull_request to "{branch}"')
def step_then_workflow_triggers_on_pr(context, branch):
    """Verify the workflow triggers on pull_request to a specific branch."""
    on_config = context.ci_workflow.get("on", context.ci_workflow.get(True, {}))
    pr_config = on_config.get("pull_request", {})
    branches = pr_config.get("branches", [])
    if branch not in branches:
        raise AssertionError(
            f"Branch '{branch}' not found in pull_request branches: {branches}"
        )


@then("at least one job should use actions/cache")
def step_then_at_least_one_job_uses_cache(context):
    """Verify at least one job uses actions/cache for dependency caching."""
    jobs = context.ci_workflow.get("jobs", {})
    for job_data in jobs.values():
        for step in job_data.get("steps", []):
            uses = step.get("uses", "")
            if "actions/cache" in uses:
                return
    raise AssertionError(
        "No job in the CI workflow uses actions/cache for dependency caching"
    )
