///|
/// Pipe-Type Multi-Stage Self-Correction Demo
///
/// Simulates an LLM agent loop where the model's text output must pass
/// through a two-stage validation pipeline:
///   1. String stage: raw output must be a non-empty string
///   2. Transform: parse the string as a JSON number
///   3. Pipe stage: the number must be an integer between 1-100
///
/// Correction prompts are stage-aware — they only report the failing
/// step, not the whole pipeline.
///
/// Run with: moon run examples/mock/pipe_agent

///|
/// Try to parse a JSON string value as a number.
fn parse_json_number(json : Json) -> Result[Json, String] {
  match json {
    String(s) => {
      let trimmed = s.trim()
      let parsed = @json.parse(trimmed) catch { _ => Json::null() }
      match parsed {
        Number(_) => Ok(parsed)
        _ => Err("cannot parse \"\{s}\" as a number")
      }
    }
    _ => Err("expected a string")
  }
}

///|
fn main {
  println(
    "╔══════════════════════════════════════════════════════════╗",
  )
  println("║   Multi-Stage Self-Correction Loop with .pipe()        ║")
  println(
    "╚══════════════════════════════════════════════════════════╝",
  )
  println("")

  // ── Two-stage validation pipeline ────────────────────────
  let score_pipeline = @moon_zod.string()
    .min(1)
    .transform(parse_json_number)
    .pipe(@moon_zod.number().int().min(1).max(100))

  // ═══════════════════════════════════════════════════════════
  // Round 1 — LLM outputs a word, not a number
  // ═══════════════════════════════════════════════════════════
  println(
    "─── [Round 1] Prompt to LLM ───────────────────────────",
  )
  println("  > Generate a single integer score between 1 and 100.")
  println("  > Return ONLY the number, no extra text.")
  println("")

  let llm_output1 = Json::string("forty-two")
  println(
    "─── [Round 1] LLM raw output ──────────────────────────",
  )
  println("  \"forty-two\"")
  println("")

  let r1_result = score_pipeline.parse(llm_output1)
  guard r1_result is Err(_) else { println("PASS (unexpected)") }
  println(
    "─── [Round 1] Validation breakdown ───────────────────",
  )
  println("  ✓ Stage 1 (string raw)       → PASS (non-empty)")
  println("  ✗ Transform (parse number)   → FAIL")
  println("  - Stage 2 (number validate)  → SKIPPED")
  println("")

  println(
    "─── [Round 1] Stage-aware correction prompt ─────────",
  )
  println("  Your response \"forty-two\" is not a valid number.")
  println("  I could not parse it as a numeric value.")
  println("  Please write a plain integer like 42, 73, or 100.")
  println("")

  // ═══════════════════════════════════════════════════════════
  // Round 2 — LLM fixes to a number, but out of range
  // ═══════════════════════════════════════════════════════════
  println("─── [Round 2] Prompt to LLM (with correction context) ──")
  println("  > Your last output could not be parsed.")
  println("  > Write a plain number between 1 and 100.")
  println("")

  let llm_output2 = Json::string("200")
  println(
    "─── [Round 2] LLM raw output ──────────────────────────",
  )
  println("  \"200\"")
  println("")

  let r2_result = score_pipeline.parse(llm_output2)
  guard r2_result is Err(errors) else { println("PASS (unexpected)") }
  let code_2 = errors[0].code
  let msg_2 = errors[0].message
  println(
    "─── [Round 2] Validation breakdown ───────────────────",
  )
  println("  ✓ Stage 1 (string raw)       → PASS")
  println("  ✓ Transform (parse number)   → PASS (parsed as 200)")
  println("  ✗ Stage 2 (number validate)  → FAIL")
  println("     \{msg_2}")
  println("     (code: \{@debug.to_string(code_2)})")
  println("")

  println(
    "─── [Round 2] Stage-aware correction prompt ─────────",
  )
  println("  You wrote \"200\", but the maximum allowed is 100.")
  println("  Your numeric value 200 exceeds the upper limit.")
  println("  Please pick a number between 1 and 100.")
  println("")

  // ═══════════════════════════════════════════════════════════
  // Round 3 — LLM finally gets it right
  // ═══════════════════════════════════════════════════════════
  println("─── [Round 3] Prompt to LLM (with correction context) ──")
  println("  > Your last output 200 was out of range.")
  println("  > Provide a number between 1 and 100.")
  println("")

  let llm_output3 = Json::string("73")
  println(
    "─── [Round 3] LLM raw output ──────────────────────────",
  )
  println("  \"73\"")
  println("")

  let r3_result = score_pipeline.parse(llm_output3)
  guard r3_result is Ok(v) else { println("FAIL (unexpected)") }
  println(
    "─── [Round 3] Validation breakdown ───────────────────",
  )
  println("  ✓ Stage 1 (string raw)       → PASS")
  println("  ✓ Transform (parse number)   → PASS (parsed as 73)")
  println("  ✓ Stage 2 (number validate)  → PASS")
  println("")
  println("  Final validated value: \{@debug.to_string(v)}")
  println("")
  println(
    "╔══════════════════════════════════════════════════════════╗",
  )
  println("║ Self-correction loop completed in 3 rounds.           ║")
  println(
    "╚══════════════════════════════════════════════════════════╝",
  )
}