///|
/// Production-Grade AI Educational Agent Showcase
///
/// Simulates a multi-round LLM self-correction loop for a curriculum
/// management tool. Demonstrates moon_zod's three-layer defense:
///
///   Layer 1 — Structural validation (type checks, enum constraints)
///   Layer 2 — Rule validation (min/max, positive, int)
///   Layer 3 — Strip cleanse (silent hallucinated-field removal)
///
/// Each round feeds validation errors back as a structured correction
/// prompt, mimicking a real LLM tool-calling retry loop.
///
/// Run with: moon run examples/educational_agent
fn main {
  println(
    "╔══════════════════════════════════════════════════════════╗",
  )
  println("║   AI Educational Agent — Resilience Loop Showcase       ║")
  println(
    "╚══════════════════════════════════════════════════════════╝",
  )
  println("")

  // ═══════════════════════════════════════════════════════════════════
  // Step 1: Define the CoursePayload Schema
  // ═══════════════════════════════════════════════════════════════════
  let course_schema = @moon_zod.object({
    "course_id": @moon_zod.number().int().min(0),
    "title": @moon_zod.string().min(5),
    "difficulty": @moon_zod.enum_values(["beginner", "intermediate", "advanced"]),
    "lessons": @moon_zod.array(
      @moon_zod.object({
        "lesson_id": @moon_zod.number().int().min(0),
        "topic": @moon_zod.string().min(1),
        "estimated_minutes": @moon_zod.number().int().positive(),
      }),
    ),
    "instructor": @moon_zod.string().optional(),
    "metadata": @moon_zod.object({
      "department": @moon_zod.string().min(1),
      "credits": @moon_zod.number().int().min(1).max(10),
    }).optional(),
  })

  println("── [Schema] CoursePayload ──")
  println("  Fields: course_id, title, difficulty, lessons[],")
  println("          instructor?, metadata?")
  println("  Rules:  title.min(5), difficulty.enum(...),")
  println("          estimated_minutes.positive(), credits.min(1).max(10)")
  println("")
  println("  JSON Schema skeleton (sent to LLM API):")
  println("  \{@debug.to_string(@moon_zod.to_json_schema(course_schema))}")
  println("")

  // ═══════════════════════════════════════════════════════════════════
  // Round 1 — Severe Structural Mutation
  // ═══════════════════════════════════════════════════════════════════
  println("── [Round 1] Severe Structural Mutation ──")
  println("  Simulating: LLM hallucinates types, invents enum values,")
  println(
    "              produces negative durations, and drops required fields.",
  )
  println("")

  let round1_json = mock_round1_response()
  println("  Raw LLM output: \{@debug.to_string(round1_json)}")
  println("")

  match course_schema.parse(round1_json) {
    Err(errors) => {
      println("  >>> moon_zod caught \{errors.length().to_string()} error(s):")
      for e in errors {
        println("      \{e.to_string()}")
      }
      println("")

      // Build a structured correction prompt from error paths
      let prompt = build_correction_prompt(errors)
      println("  >>> Generated Correction Prompt (sent back to LLM):")
      println(
        "  ┌────────────────────────────────────────────────────",
      )
      println(prompt)
      println(
        "  └────────────────────────────────────────────────────",
      )
      println("")

      // ── LLM retries with corrected JSON ──────────────────────
      let round2_json = mock_round2_response()
      println("── [Round 2] LLM Retry (Schema Violation & Recovery) ──")
      println("  Simulating: LLM fixes structural types but violates")
      println("              rule constraints (title too short).")
      println("")
      println("  Raw LLM output: \{@debug.to_string(round2_json)}")
      println("")

      match course_schema.parse(round2_json) {
        Err(errors2) => {
          println(
            "  >>> moon_zod caught \{errors2.length().to_string()} rule-level error(s):",
          )
          for e in errors2 {
            println("      \{e.to_string()}")
          }
          println("")
          println(
            "  >>> This is moon_zod's SECOND line of defense: even when types",
          )
          println(
            "      are correct, rule constraints (min, max, positive, etc.)",
          )
          println("      catch violations before data enters the system.")
          println("")

          let prompt2 = build_correction_prompt(errors2)
          println("  >>> Refined Correction Prompt:")
          println(
            "  ┌────────────────────────────────────────────────────",
          )
          println(prompt2)
          println(
            "  └────────────────────────────────────────────────────",
          )
          println("")

          // ── LLM retries again ──────────────────────────────────
          println("── [Round 3] Final Retry + Strip Cleanse ──")
          println("  Simulating: LLM returns structurally perfect JSON")
          println("              with 12 hallucinated metadata fields.")
          println("")

          let round3_json = mock_round3_response()
          println("  Raw LLM output: \{@debug.to_string(round3_json)}")
          println("")

          match course_schema.parse(round3_json) {
            Ok(clean) => {
              println("  >>> Validation passed! (Strip mode active)")
              match clean {
                Object(map) => {
                  let mut field_count = 0
                  for _ in map.keys() {
                    field_count = field_count + 1
                  }
                  println(
                    "  >>> Stripped output (\{field_count.to_string()} spec fields, 12 hallucinated fields removed):",
                  )
                }
                _ => ()
              }
              println("  \{@debug.to_string(clean)}")
              println("")
              println(
                "  >>> The LLM's hallucinated fields (llm_model, temperature,",
              )
              println(
                "      top_p, seed, etc.) were silently stripped by moon_zod's",
              )
              println(
                "      default Strip mode — the downstream system never sees them.",
              )
              println("")
              println(
                "╔══════════════════════════════════════════════════════════╗",
              )
              println(
                "║   SUCCESS: 3-round resilience loop completed.           ║",
              )
              println(
                "║   Layer 1 (types) → Layer 2 (rules) → Layer 3 (strip)   ║",
              )
              println(
                "╚══════════════════════════════════════════════════════════╝",
              )
            }
            Err(errors3) => {
              println("  >>> Still invalid after second retry:")
              for e in errors3 {
                println("      [\{e.path}] \{e.message}")
              }
            }
          }
        }
        Ok(_) => println("  Unexpected: round 2 passed (should have failed)")
      }
    }
    Ok(_) => println("  Unexpected: round 1 passed (should have failed)")
  }
}

///|
/// Build a structured correction prompt from validation errors.
/// Each error's path (formatted by moon_zod's internal format_path)
/// pinpoints the exact location for the LLM to fix.
fn build_correction_prompt(errors : Array[@moon_zod.ValidationError]) -> String {
  let mut prompt = "## Validation Errors Detected\n\n"
  prompt = prompt + "Your previous JSON output failed schema validation. "
  prompt = prompt +
    "The errors below show the exact field path, the constraint violated, and the value you provided.\n\n"
  prompt = prompt + "### Errors to fix:\n\n"
  for e in errors {
    prompt = prompt + "- " + e.to_string() + "\n"
  }
  prompt = prompt + "\n### Instructions:\n\n"
  prompt = prompt + "1. Fix each error at the exact path shown above\n"
  prompt = prompt + "2. Ensure all enum values match the allowed set\n"
  prompt = prompt + "3. Verify numeric constraints (min, max, positive, int)\n"
  prompt = prompt + "4. Include ALL required fields\n"
  prompt = prompt + "5. Return ONLY the corrected JSON object, no extra text\n"
  prompt
}

///|
/// Round 1 mock: severely malformed JSON.
///   - course_id is a string instead of number (type error)
///   - difficulty is "expert" (not in enum)
///   - title is missing (required field)
///   - lessons[0].estimated_minutes is -30 (violates positive())
///   - lessons[1].topic is empty (violates min(1))
///   - lessons[2] is missing lesson_id (required field)
fn mock_round1_response() -> Json {
  Json::object({
    "course_id": Json::string("CS101"),
    "difficulty": Json::string("expert"),
    "lessons": Json::array([
      Json::object({
        "lesson_id": Json::number(1.0),
        "topic": Json::string("Introduction to Algorithms"),
        "estimated_minutes": Json::number(-30.0),
      }),
      Json::object({
        "lesson_id": Json::number(2.0),
        "topic": Json::string(""),
        "estimated_minutes": Json::number(45.0),
      }),
      Json::object({
        "topic": Json::string("Graph Theory"),
        "estimated_minutes": Json::number(60.0),
      }),
    ]),
    "instructor": Json::string("Dr. Smith"),
    "metadata": Json::object({
      "department": Json::string("Computer Science"),
      "credits": Json::number(3.0),
    }),
  })
}

///|
/// Round 2 mock: structural types fixed but rule violations remain.
///   - title is "Math" (too short, min(5) violation)
///   - All other fields now have correct types and enum values
fn mock_round2_response() -> Json {
  Json::object({
    "course_id": Json::number(101.0),
    "title": Json::string("Math"),
    "difficulty": Json::string("intermediate"),
    "lessons": Json::array([
      Json::object({
        "lesson_id": Json::number(1.0),
        "topic": Json::string("Linear Algebra Basics"),
        "estimated_minutes": Json::number(45.0),
      }),
      Json::object({
        "lesson_id": Json::number(2.0),
        "topic": Json::string("Matrix Operations"),
        "estimated_minutes": Json::number(60.0),
      }),
    ]),
    "instructor": Json::string("Dr. Smith"),
    "metadata": Json::object({
      "department": Json::string("Mathematics"),
      "credits": Json::number(4.0),
    }),
  })
}

///|
/// Round 3 mock: structurally perfect + 12 hallucinated LLM metadata fields.
/// moon_zod's default Strip mode silently removes them all.
fn mock_round3_response() -> Json {
  let map : Map[String, Json] = {
    "course_id": Json::number(101.0),
    "title": Json::string("Mathematics for Computer Science"),
    "difficulty": Json::string("intermediate"),
    "lessons": Json::array([
      Json::object({
        "lesson_id": Json::number(1.0),
        "topic": Json::string("Linear Algebra Basics"),
        "estimated_minutes": Json::number(45.0),
      }),
      Json::object({
        "lesson_id": Json::number(2.0),
        "topic": Json::string("Matrix Operations"),
        "estimated_minutes": Json::number(60.0),
      }),
    ]),
    "instructor": Json::string("Dr. Smith"),
    "metadata": Json::object({
      "department": Json::string("Mathematics"),
      "credits": Json::number(4.0),
    }),
  }
  // Inject 12 hallucinated LLM metadata fields
  map.set("llm_model", Json::string("gpt-5-turbo"))
  map.set("generated_at", Json::string("2026-06-06T12:00:00Z"))
  map.set("confidence_score", Json::number(0.97))
  map.set("internal_id", Json::string("req_x9f2a1b"))
  map.set("cache_key", Json::string("ck_cache_v3"))
  map.set("request_id", Json::string("rid_7712ab34"))
  map.set("temperature", Json::number(0.7))
  map.set("top_p", Json::number(0.95))
  map.set("max_tokens", Json::number(4096.0))
  map.set("seed", Json::number(42.0))
  map.set("stream_flag", Json::boolean(false))
  map.set("version_tag", Json::string("v2.1.0-beta"))
  Json::object(map)
}