///|
/// LLM Tool Calling Self-Correction Demo
///
/// Simulates an AI agent loop:
///   1. Define a tool schema → export JSON Schema skeleton
///   2. Mock LLM call → returns invalid JSON
///   3. Validate with moon_zod → catch & format errors
///   4. Feed errors back → LLM retries with corrected JSON
///   5. Strip mode auto-cleans hallucinated fields
///
/// Run with: moon run examples/llm_agent
fn main {
  // ── Step 1: Define a realistic tool schema ──────────────────────
  let user_schema = @moon_zod.object({
    "name": @moon_zod.string().min(1).max(100),
    "age": @moon_zod.number().int().min(0).max(150),
    "email": @moon_zod.string().email(),
    "role": @moon_zod.enum_values(["admin", "user", "guest"]),
    "metadata": @moon_zod.object({
      "department": @moon_zod.string().min(1),
      "level": @moon_zod.number().int().min(1).max(10),
    }).optional(),
  })

  // Export JSON Schema skeleton (what you'd send to an LLM API)
  println("=== [Tool Schema] JSON Skeleton (sent to LLM) ===")
  println(@debug.to_string(@moon_zod.to_json_schema(user_schema)))
  println("")

  // ── Step 2: First LLM call — returns MALFORMED JSON ────────────
  println("=== [Round 1] LLM returns invalid JSON ===")
  let bad_json = mock_bad_response()
  println("Raw output: \{@debug.to_string(bad_json)}")
  println("")

  // ── Step 3: Validate & format errors ──────────────────────────
  let parse_result = user_schema.parse(bad_json)
  match parse_result {
    Err(errors) => {
      println(">>> moon_zod caught \{errors.length().to_string()} error(s):")

      let error_lines : Array[String] = []
      for e in errors {
        error_lines.push("  - \{e.path}: \{e.message}")
      }
      let error_text = error_lines.join("\n")
      println(error_text)
      println("")

      // ── Step 4: Feed errors back → LLM retries ────────────────
      let correction_prompt = "Your previous JSON was invalid.\nErrors:\n" +
        error_text +
        "\nPlease fix it and return ONLY valid JSON."

      println("=== [Retry] Correction prompt sent to LLM ===")
      println("\{correction_prompt}")
      println("")

      let fixed_json = mock_fixed_response()
      println("=== [Round 2] LLM returns corrected JSON ===")
      println("Raw output: \{@debug.to_string(fixed_json)}")
      println("")

      let retry_result = user_schema.parse(fixed_json)
      match retry_result {
        Ok(clean) => {
          // Strip mode has already removed hallucinated fields
          println(
            ">>> Validation passed! Clean data (hallucinations stripped):",
          )
          println(@debug.to_string(clean))
          println("")
          println("=== Self-correction loop completed successfully ===")
        }
        Err(errors2) => {
          println(">>> Still invalid after retry — would loop again.")
          for e in errors2 {
            println("  - \{e.path}: \{e.message}")
          }
        }
      }
    }
    Ok(_) => println("Unexpected: bad JSON passed validation")
  }
}

///|
/// Mock LLM response — returns JSON with deliberate errors:
///   - "age" is a string instead of number
///   - "role" is not in the allowed enum
///   - "email" is missing (required field)
fn mock_bad_response() -> Json {
  Json::object({
    "name": Json::string("Alice"),
    "age": Json::string("twenty"),
    "role": Json::string("superadmin"),
    "metadata": Json::object({
      "department": Json::string("Engineering"),
      "level": Json::number(5.0),
    }),
  })
}

///|
/// Mock LLM retry — returns valid JSON with a hallucinated extra field.
/// moon_zod's default Strip mode will remove "extra_hallucinated_field".
fn mock_fixed_response() -> Json {
  Json::object({
    "name": Json::string("Alice"),
    "age": Json::number(30.0),
    "email": Json::string("alice@example.com"),
    "role": Json::string("admin"),
    "metadata": Json::object({
      "department": Json::string("Engineering"),
      "level": Json::number(5.0),
    }),
    "extra_hallucinated_field": Json::string("LLM imagined this"),
  })
}