"""Scope logic — ported from mgf-standard ``test_audit_secret_scopes.py`` (SPEC §7.2).

The behaviour under test is the CIR-02 shape (a secret referenced on an event it
is not scoped for). This pins the stdlib pipeline parser against every real fleet
shape and the scope comparison, so doctor C2 stays parity-identical to the audit.
"""

from __future__ import annotations

from mgf.secrets.pipeline import audit_refs, parse_pipeline

CLEAN_CRON = """\
when:
  # cadence lives server-side
  - event: cron
  - event: manual

clone:
  - name: clone
    image: woodpeckerci/plugin-git:2.9.2
    settings:
      partial: false
      use-ssh: true
      ssh-key-private:
        from_secret: codeberg_ssh_clone_key
      ssh-host-key:
        from_secret: codeberg_ssh_host_key

steps:
  - name: cost
    image: python:3.12-slim
    environment:
      CLOUDS_YAML:
        from_secret: clouds_yaml
    commands:
      - echo hi
"""

WITH_PUBLISH = """\
when:
  - event: push
    branch: main
  - event: pull_request
  - event: tag
    ref: refs/tags/v*
  - event: manual

clone:
  - name: clone
    settings:
      ssh-key-private:
        from_secret: codeberg_ssh_clone_key

steps:
  - name: lint
    commands:
      - ruff check src
  - name: publish
    when:
      event: tag
      ref: refs/tags/v*
    environment:
      TWINE_PASSWORD:
        from_secret: devpi_password
    commands:
      - twine upload dist/*
"""

INLINE_FLOW = """\
when:
  - event: push
  - event: tag

steps:
  - name: publish
    when: { event: tag, ref: refs/tags/v* }
    environment:
      TWINE_PASSWORD: { from_secret: devpi_password }
    commands:
      - twine upload dist/*
"""

EVENT_FLOW_LIST = """\
when:
  - event: [push, pull_request]
  - event: manual

steps:
  - name: build
    environment:
      TOKEN:
        from_secret: sibling_clone_token
    commands:
      - echo build
"""

SECRETS_LIST_FORM = """\
when:
  - event: push

steps:
  - name: deploy
    secrets: [alpha_key, beta_key]
    commands:
      - echo deploy
  - name: deploy2
    secrets:
      - gamma_key
    commands:
      - echo deploy2
"""

COMMENT_TRAP = """\
when:
  - event: push

steps:
  # No `tag` event: this repo publishes no versioned artifact to any index.
  - name: build
    environment:
      TOKEN:
        from_secret: codeberg_repo_read
    commands:
      - echo build
"""


def _refs(text: str) -> dict[str, set[str]]:
    _, refs = parse_pipeline(text)
    out: dict[str, set[str]] = {}
    for r in refs:
        out.setdefault(r.secret, set()).update(r.events)
    return out


def test_clone_secrets_inherit_pipeline_when_events() -> None:
    pe, _ = parse_pipeline(CLEAN_CRON)
    assert pe == frozenset({"cron", "manual"})
    got = _refs(CLEAN_CRON)
    assert got["codeberg_ssh_clone_key"] == {"cron", "manual"}
    assert got["clouds_yaml"] == {"cron", "manual"}


def test_step_level_when_narrows_secret() -> None:
    got = _refs(WITH_PUBLISH)
    assert got["codeberg_ssh_clone_key"] == {"push", "pull_request", "tag", "manual"}
    assert got["devpi_password"] == {"tag"}


def test_inline_flow_forms() -> None:
    assert _refs(INLINE_FLOW) == {"devpi_password": {"tag"}}


def test_event_flow_list_expands() -> None:
    assert _refs(EVENT_FLOW_LIST)["sibling_clone_token"] == {"push", "pull_request", "manual"}


def test_secrets_list_form_block_and_flow() -> None:
    got = _refs(SECRETS_LIST_FORM)
    assert got["alpha_key"] == {"push"}
    assert got["beta_key"] == {"push"}
    assert got["gamma_key"] == {"push"}


def test_comment_event_is_not_counted() -> None:
    pe, _ = parse_pipeline(COMMENT_TRAP)
    assert pe == frozenset({"push"})
    assert _refs(COMMENT_TRAP)["codeberg_repo_read"] == {"push"}


def test_clean_when_every_event_allowed() -> None:
    _, refs = parse_pipeline(CLEAN_CRON)
    allowed = {
        "codeberg_ssh_clone_key": frozenset({"cron", "manual", "push", "pull_request", "tag"}),
        "codeberg_ssh_host_key": frozenset({"cron", "manual", "push", "pull_request", "tag"}),
        "clouds_yaml": frozenset({"cron", "manual"}),
    }
    assert audit_refs(refs, allowed) == []


def test_historical_cost_watch_mismatch_is_flagged() -> None:
    _, refs = parse_pipeline(CLEAN_CRON)
    allowed = {
        "codeberg_ssh_clone_key": frozenset({"push", "pull_request", "tag"}),
        "codeberg_ssh_host_key": frozenset({"push", "pull_request", "tag"}),
        "clouds_yaml": frozenset({"cron", "manual"}),
    }
    findings = audit_refs(refs, allowed)
    by_secret = {f.secret: f for f in findings}
    assert set(by_secret) == {"codeberg_ssh_clone_key", "codeberg_ssh_host_key"}
    f = by_secret["codeberg_ssh_clone_key"]
    assert f.kind == "scope"
    assert f.bad_events == ["cron", "manual"]


def test_secret_defined_nowhere_is_undefined() -> None:
    _, refs = parse_pipeline(EVENT_FLOW_LIST)
    findings = audit_refs(refs, {})
    assert len(findings) == 1
    assert findings[0].kind == "undefined"
    assert "NEITHER" in findings[0].describe()


def test_repo_secret_shadows_org_secret() -> None:
    org = {"sibling_clone_token": frozenset({"push"})}
    repo = {"sibling_clone_token": frozenset({"push", "pull_request", "manual"})}
    _, refs = parse_pipeline(EVENT_FLOW_LIST)
    assert audit_refs(refs, {**org, **repo}) == []
