///|
/// A reproducible application-level scenario result.
pub(all) struct ScenarioReport {
  name : String
  documents : Int
  queries : Int
  successful_queries : Int
  average_recall : Double
  context_sources : Int
  passed : Bool
  notes : String
}

///|
pub impl Show for ScenarioReport with fn output(self, logger) {
  logger.write_string(
    "ScenarioReport{name: " +
    self.name +
    ", documents: " +
    self.documents.to_string() +
    ", queries: " +
    self.queries.to_string() +
    ", successful: " +
    self.successful_queries.to_string() +
    ", recall: " +
    self.average_recall.to_string() +
    ", context_sources: " +
    self.context_sources.to_string() +
    ", passed: " +
    self.passed.to_string() +
    "}",
  )
}

///|
/// Documents representing a small multi-tenant knowledge base.
pub fn knowledge_base_scenario_documents() -> Array[Document] {
  [
    Document::new("kb-auth", [0.98, 0.12, 0.02, 0.01], [
      ("tenant", "acme"),
      ("topic", "security"),
      ("title", "Authentication"),
      (
        "text", "Use token rotation and short session lifetimes for authentication.",
      ),
      ("source", "security-handbook"),
    ]),
    Document::new("kb-rag", [0.94, 0.18, 0.03, 0.02], [
      ("tenant", "acme"),
      ("topic", "retrieval"),
      ("title", "Retrieval pipeline"),
      (
        "text", "Retrieve relevant chunks, rerank them, and cite the source documents.",
      ),
      ("source", "ai-playbook"),
    ]),
    Document::new("kb-cache", [0.88, 0.22, 0.06, 0.04], [
      ("tenant", "acme"),
      ("topic", "performance"),
      ("title", "Embedding cache"),
      (
        "text", "Cache embeddings by model and document revision to reduce repeated work.",
      ),
      ("source", "platform-guide"),
    ]),
    Document::new("kb-index", [0.82, 0.28, 0.08, 0.05], [
      ("tenant", "acme"),
      ("topic", "retrieval"),
      ("title", "Index selection"),
      (
        "text", "Use Flat for exact baselines, IVF for clustered search, and LSH for fast approximate candidates.",
      ),
      ("source", "ai-playbook"),
    ]),
    Document::new("kb-privacy", [0.16, 0.92, 0.05, 0.03], [
      ("tenant", "acme"),
      ("topic", "security"),
      ("title", "Privacy review"),
      (
        "text", "Remove personal data from logs and enforce tenant boundaries before retrieval.",
      ),
      ("source", "security-handbook"),
    ]),
    Document::new("kb-observe", [0.22, 0.84, 0.08, 0.04], [
      ("tenant", "acme"),
      ("topic", "operations"),
      ("title", "Observability"),
      (
        "text", "Track query counts, empty results, recall, and index rebuild revisions.",
      ),
      ("source", "platform-guide"),
    ]),
    Document::new("kb-fallback", [0.28, 0.78, 0.11, 0.05], [
      ("tenant", "acme"),
      ("topic", "operations"),
      ("title", "Fallback search"),
      (
        "text", "Fall back to the exact index when an approximate index is empty or stale.",
      ),
      ("source", "platform-guide"),
    ]),
    Document::new("kb-eval", [0.34, 0.72, 0.14, 0.07], [
      ("tenant", "acme"),
      ("topic", "evaluation"),
      ("title", "Recall evaluation"),
      (
        "text", "Compare approximate results with Flat using recall at several cutoffs.",
      ),
      ("source", "ai-playbook"),
    ]),
    Document::new("kb-tenant", [0.12, 0.22, 0.95, 0.04], [
      ("tenant", "beta"),
      ("topic", "security"),
      ("title", "Tenant isolation"),
      (
        "text", "Apply tenant metadata filters before returning results to clients.",
      ),
      ("source", "beta-handbook"),
    ]),
    Document::new("kb-schema", [0.16, 0.26, 0.88, 0.08], [
      ("tenant", "beta"),
      ("topic", "data"),
      ("title", "Schema validation"),
      (
        "text", "Reject empty vectors, duplicate ids, and inconsistent dimensions at ingestion.",
      ),
      ("source", "beta-handbook"),
    ]),
    Document::new("kb-batch", [0.18, 0.30, 0.82, 0.10], [
      ("tenant", "beta"),
      ("topic", "performance"),
      ("title", "Batch retrieval"),
      (
        "text", "Use batch search to amortize application overhead for multiple queries.",
      ),
      ("source", "beta-handbook"),
    ]),
    Document::new("kb-audit", [0.20, 0.34, 0.76, 0.12], [
      ("tenant", "beta"),
      ("topic", "operations"),
      ("title", "Audit trail"),
      (
        "text", "Record the index strategy, filter expression, revision, and source ids.",
      ),
      ("source", "beta-handbook"),
    ]),
  ]
}

///|
/// Product catalog documents for filtered recommendation tests.
pub fn catalog_scenario_documents() -> Array[Document] {
  [
    Document::new("p-laptop-1", [0.91, 0.82, 0.30, 0.10], [
      ("category", "laptop"),
      ("brand", "moon"),
      ("price", "premium"),
      ("title", "MoonBook Pro"),
      ("text", "portable laptop for engineering and data work"),
    ]),
    Document::new("p-laptop-2", [0.88, 0.78, 0.34, 0.12], [
      ("category", "laptop"),
      ("brand", "orbit"),
      ("price", "mid"),
      ("title", "Orbit Air"),
      ("text", "lightweight laptop for travel and development"),
    ]),
    Document::new("p-laptop-3", [0.84, 0.76, 0.28, 0.16], [
      ("category", "laptop"),
      ("brand", "moon"),
      ("price", "mid"),
      ("title", "MoonBook Studio"),
      ("text", "developer laptop with a bright display"),
    ]),
    Document::new("p-laptop-4", [0.80, 0.70, 0.40, 0.18], [
      ("category", "laptop"),
      ("brand", "terra"),
      ("price", "budget"),
      ("title", "Terra Code"),
      ("text", "budget laptop for programming students"),
    ]),
    Document::new("p-phone-1", [0.12, 0.24, 0.92, 0.14], [
      ("category", "phone"),
      ("brand", "moon"),
      ("price", "premium"),
      ("title", "MoonPhone X"),
      ("text", "secure phone with a long battery life"),
    ]),
    Document::new("p-phone-2", [0.18, 0.28, 0.86, 0.18], [
      ("category", "phone"),
      ("brand", "orbit"),
      ("price", "mid"),
      ("title", "Orbit Note"),
      ("text", "phone for notes, calls, and everyday use"),
    ]),
    Document::new("p-phone-3", [0.22, 0.30, 0.82, 0.22], [
      ("category", "phone"),
      ("brand", "terra"),
      ("price", "budget"),
      ("title", "Terra Mini"),
      ("text", "compact affordable phone"),
    ]),
    Document::new("p-phone-4", [0.16, 0.20, 0.78, 0.20], [
      ("category", "phone"),
      ("brand", "moon"),
      ("price", "mid"),
      ("title", "MoonPhone S"),
      ("text", "balanced phone with strong privacy controls"),
    ]),
    Document::new("p-audio-1", [0.24, 0.18, 0.42, 0.90], [
      ("category", "audio"),
      ("brand", "moon"),
      ("price", "premium"),
      ("title", "MoonPods Max"),
      ("text", "noise cancelling headphones for focused work"),
    ]),
    Document::new("p-audio-2", [0.28, 0.22, 0.38, 0.84], [
      ("category", "audio"),
      ("brand", "orbit"),
      ("price", "mid"),
      ("title", "Orbit Buds"),
      ("text", "wireless earbuds for commuting"),
    ]),
    Document::new("p-audio-3", [0.32, 0.26, 0.34, 0.78], [
      ("category", "audio"),
      ("brand", "terra"),
      ("price", "budget"),
      ("title", "Terra Sound"),
      ("text", "affordable headphones for calls"),
    ]),
    Document::new("p-audio-4", [0.26, 0.20, 0.46, 0.74], [
      ("category", "audio"),
      ("brand", "moon"),
      ("price", "mid"),
      ("title", "MoonPods S"),
      ("text", "comfortable earbuds with secure fit"),
    ]),
  ]
}

///|
/// Run a retrieval regression scenario against the exact Flat baseline.
pub fn run_retrieval_scenario(
  name : String,
  docs : Array[Document],
  queries : Array[Array[Double]],
  top_k : Int,
) -> ScenarioReport raise VectorError {
  validate_documents(docs)
  let index = build_flat_index(docs)
  let successful = []
  for query in queries {
    let results = index.search(query, top_k, Cosine, [])
    successful.push(results.length() > 0)
  }
  let mut success_count = 0
  for ok in successful {
    if ok {
      success_count = success_count + 1
    }
  }
  let response_count = if queries.length() == 0 {
    0.0
  } else {
    success_count.to_double() / queries.length().to_double()
  }
  {
    name,
    documents: docs.length(),
    queries: queries.length(),
    successful_queries: success_count,
    average_recall: response_count,
    context_sources: 0,
    passed: success_count == queries.length(),
    notes: "exact retrieval baseline",
  }
}

///|
/// Exercise a tenant-aware RAG workflow with source attribution.
pub fn run_rag_scenario() -> ScenarioReport raise VectorError {
  let docs = knowledge_base_scenario_documents()
  let tenant_docs = filter_documents(docs, [("tenant", "acme")])
  let context = build_rag_context(
    tenant_docs,
    [0.90, 0.20, 0.04, 0.03],
    "retrieval source documents",
    4,
    0.75,
    480,
  )
  {
    name: "tenant-rag",
    documents: tenant_docs.length(),
    queries: 1,
    successful_queries: if context.chunks.length() > 0 {
      1
    } else {
      0
    },
    average_recall: if context.chunks.length() > 0 {
      1.0
    } else {
      0.0
    },
    context_sources: context.source_ids.length(),
    passed: context.chunks.length() > 0 && context.text.length() <= 600,
    notes: context_citations(context),
  }
}

///|
/// Exercise category and price filters in a catalog search.
pub fn run_catalog_scenario() -> ScenarioReport raise VectorError {
  let docs = catalog_scenario_documents()
  let laptops = filter_documents(docs, [
    ("category", "laptop"),
    ("price", "mid"),
  ])
  let index = build_flat_index(laptops)
  let results = index.search([0.88, 0.80, 0.32, 0.12], 3, Cosine, [])
  {
    name: "catalog-filtered-recommendation",
    documents: docs.length(),
    queries: 1,
    successful_queries: if results.length() == 2 {
      1
    } else {
      0
    },
    average_recall: if results.length() == 2 {
      1.0
    } else {
      0.0
    },
    context_sources: 0,
    passed: results.length() == 2,
    notes: "category=laptop AND price=mid",
  }
}

///|
/// Exercise batch retrieval and recall reporting for another application path.
pub fn run_batch_scenario() -> ScenarioReport raise VectorError {
  let docs = knowledge_base_scenario_documents()
  let index = build_flat_index(docs)
  let queries = [
    [0.95, 0.15, 0.03, 0.02],
    [0.18, 0.28, 0.90, 0.12],
    [0.20, 0.30, 0.80, 0.10],
  ]
  let results = index.search_batch(queries, 3, Cosine, [])
  let telemetry = summarize_workload(results, 3)
  {
    name: "batch-tenant-routing",
    documents: docs.length(),
    queries: queries.length(),
    successful_queries: telemetry.queries - telemetry.empty_queries,
    average_recall: if telemetry.queries == 0 {
      0.0
    } else {
      1.0 - telemetry.empty_queries.to_double() / telemetry.queries.to_double()
    },
    context_sources: telemetry.returned,
    passed: workload_is_stable(telemetry, 3),
    notes: "batch search with workload telemetry",
  }
}

///|
/// Run all included application scenarios.
pub fn run_all_application_scenarios() -> Array[ScenarioReport] raise VectorError {
  [run_rag_scenario(), run_catalog_scenario(), run_batch_scenario()]
}