#!/usr/bin/env python3
"""
Pipeline Demo — end-to-end walkthrough of all pipeline modules.

Demonstrates:
  1. Confidence Ladder (5-level Verdict)
  2. Rejection Envelope (4-field blocked action)
  3. State Gate (pre-merge completeness check)
  4. Expertise file (per-cron compounding memory)

Run: python -m pipeline.demo
"""
import json
import sys
import os

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from pipeline.confidence_ladder import Verdict, VerdictLevel, VerdictReport
from pipeline.rejection_envelope import RejectionEnvelope, Blocker
from pipeline.state_gate import StateGate, FieldSpec, PipelineState
from pipeline.expertise import append_entry, render_context, purge_domain


def hr(title: str):
    print(f"\n{'=' * 60}")
    print(f"  {title}")
    print(f"{'=' * 60}")


# ═══════════════════════════════════════════════════════════════════════════════
# 1. STATE INIT
# ═══════════════════════════════════════════════════════════════════════════════
hr("1. STATE INIT — PipelineState with core_fields")

state = PipelineState(
    {
        "plan_file": "plans/feat-123.md",
        "branch_name": "feat/instant-polls",
        "issue_number": 42,
        "rogue_field": "should be silently dropped",
    },
    core_fields=["plan_file", "branch_name", "issue_number", "backend_port", "frontend_port"],
)
print(f"  plan_file:     {state.get('plan_file')}")
print(f"  branch_name:   {state.get('branch_name')}")
print(f"  issue_number:  {state.get('issue_number')}")
print(f"  rogue dropped: {state.get('rogue_field')}")  # should be None
assert state.get("rogue_field") is None, "rogue fields are silently dropped"

# ═══════════════════════════════════════════════════════════════════════════════
# 2. PLAN PHASE — PERFECT
# ═══════════════════════════════════════════════════════════════════════════════
hr("2. PLAN PHASE — Verdict: PERFECT")

report = VerdictReport(pipeline_id="demo-2026-06-11")
report.add(Verdict(
    VerdictLevel.PERFECT,
    "Plan covers all acceptance criteria with test coverage map",
    evidence=["AC1 coverage: tests/test_instant_polls.py", "AC2 coverage: tests/test_instant_polls.py"],
    source="plan-review",
))
print(f"  Ve rdict: {report.aggregate_label}")
print(f"  Pass?    {report.passed}")

# ═══════════════════════════════════════════════════════════════════════════════
# 3. BUILD PHASE — VERIFIED
# ═══════════════════════════════════════════════════════════════════════════════
hr("3. BUILD PHASE — Verdict: VERIFIED")

state.update(backend_port=9105, frontend_port=9205)
report.add(Verdict(
    VerdictLevel.VERIFIED,
    "Build completed, CI confirmed all 14 tests pass",
    evidence=["exit code 0", "CI run #4185: all tests green"],
    source="ci",
))
print(f"  Verdict: {report.aggregate_label}")
print(f"  Pass?    {report.passed}")
print(f"  Verdicts: {len(report.verdicts)} phases so far")

# ═══════════════════════════════════════════════════════════════════════════════
# 4. SECURITY REVIEW — PARTIAL (the critical gate)
# ═══════════════════════════════════════════════════════════════════════════════
hr("4. SECURITY REVIEW — Verdict: PARTIAL (green-but-dead check)")

report.add(Verdict(
    VerdictLevel.PARTIAL,
    "Static analysis clean, dependency scan clean — but no live penetration test was run",
    evidence=["semgrep: 0 findings", "pip audit: 0 known vulns", "NOTE: no live pen test — needs staging env"],
    source="security-review",
))
print(f"  Verdict: {report.aggregate_label}")
print(f"  Pass?    {report.passed}  (PARTIAL = green + documented gap)")
print(f"  Blocked for merge? {report.aggregate.is_blocking}")
print()
print("  ▶ THIS is the green-but-dead prevention:")
print("    The pipeline passes (green) but the security gap is")
print("    documented, not forgotten. The reviewer knows what's missing.")
assert report.aggregate == VerdictLevel.PARTIAL
assert report.passed is True
assert report.aggregate.is_blocking is False

# ═══════════════════════════════════════════════════════════════════════════════
# 5. REJECTION ENVELOPE — leader tries to write a file
# ═══════════════════════════════════════════════════════════════════════════════
hr("5. REJECTION ENVELOPE — leader blocked from writing files")

blocker = Blocker(
    name="no_direct_write",
    match=lambda a: a.get("tool") in ("write_file", "patch", "execute_code"),
    reject=lambda a: RejectionEnvelope.block(
        received=a.get("tool", "unknown"),
        reason="LEADER_MUST_NOT_WRITE_FILES",
        allowed="Use delegate_task with a writer subagent",
        hint="Leaders read, delegate, synthesize. Delegate file writes.",
    ),
)

# This gets blocked
blocked = blocker.evaluate({"tool": "write_file", "path": "src/main.py"})
print(f"  Action: write_file('src/main.py')")
print(f"  Result: BLOCKED")
print(f"  Reason: {blocked.reason}")
print(f"  Allowed: {blocked.allowed}")
print(f"  Hint: {blocked.hint}")
print(f"  Timestamp: {blocked.timestamp}")
assert blocked is not None

# This passes through
allowed = blocker.evaluate({"tool": "read_file", "path": "state.json"})
print(f"\n  Action: read_file('state.json')")
print(f"  Result: {'PASSES THROUGH (not blocked)' if allowed is None else 'BLOCKED'}")

# ═══════════════════════════════════════════════════════════════════════════════
# 6. STATE GATE — pre-merge completeness check
# ═══════════════════════════════════════════════════════════════════════════════
hr("6. STATE GATE — pre-merge completeness check")

ship_gate = StateGate(
    FieldSpec("plan_file", required=True, predicate=lambda v: isinstance(v, str) and v.startswith("plans/")),
    FieldSpec("branch_name", required=True),
    FieldSpec("issue_number", required=True, type_hint=int),
    FieldSpec("backend_port", required=True, type_hint=int),
    FieldSpec("frontend_port", required=True, type_hint=int),
    label="pre-ship",
)

# Complete state should pass
result = ship_gate.validate(state)
print(f"  Fields checked: {result.fields_checked}")
print(f"  Pass: {result.passed}")
if result.missing:
    print(f"  Missing: {result.missing}")
assert result.passed, "Complete state should pass ship gate"

# Incomplete state should fail
incomplete = PipelineState(
    {"plan_file": "plans/feat-123.md", "branch_name": "feat/instant-polls"},
    core_fields=["plan_file", "branch_name", "issue_number", "backend_port", "frontend_port"],
)
fail_result = ship_gate.validate(incomplete)
print(f"\n  --- With missing fields ---")
print(f"  Pass: {fail_result.passed}")
print(f"  Missing: {fail_result.missing}")
assert not fail_result.passed
assert "issue_number" in fail_result.missing

# ═══════════════════════════════════════════════════════════════════════════════
# 7. EXPERTISE — store what was learned
# ═══════════════════════════════════════════════════════════════════════════════
hr("7. EXPERTISE — store what the run learned")

append_entry(
    "demo-pipeline",
    domain="pipeline",
    insight="PARTIAL verdict is the key innovation — it lets documented gaps pass without blocking, preventing green-but-dead",
    source="cron:pipeline-demo",
    verified=True,
)
append_entry(
    "demo-pipeline",
    domain="confidence-ladder",
    insight="VERIFIED level requires independent confirmation (CI, review) — the agent trusting itself is only PERFECT",
    source="cron:pipeline-demo",
    verified=True,
)
append_entry(
    "demo-pipeline",
    domain="state-gate",
    insight="Core_fields filter ensures no rogue keys leak into pipeline state — strict struct, not a dict",
    source="cron:pipeline-demo",
    verified=False,
)

print(render_context("demo-pipeline"))

# ═══════════════════════════════════════════════════════════════════════════════
# 8. JSON PORTABILITY
# ═══════════════════════════════════════════════════════════════════════════════
hr("8. JSON PORTABILITY — serializable for cron/API delivery")

serialized = report.to_json()
restored = VerdictReport.from_json(serialized)
print(f"  Round-trip aggregate matches: {restored.aggregate == report.aggregate}")
print(f"  Verdict count: {len(restored.verdicts)}")
print(f"\n  Final JSON payload (compact):")
print(f"  {json.dumps(json.loads(serialized), indent=2)}")

# ═══════════════════════════════════════════════════════════════════════════════
# CLEANUP
# ═══════════════════════════════════════════════════════════════════════════════
purge_domain("demo-pipeline", "pipeline")
purge_domain("demo-pipeline", "confidence-ladder")
purge_domain("demo-pipeline", "state-gate")

if __name__ == "__main__":

    print(f"\n{'=' * 60}")
    print(f"  ✅ DEMO COMPLETE — all 8 stages verified")
    print(f"{'=' * 60}")
