///|
pub struct PathExpectation {
  path : String
  allowed : Bool
} derive(Debug, Eq)

///|
pub struct Scenario {
  name : String
  robots : String
  agent : String
  expectations : Array[PathExpectation]
} derive(Debug, Eq)

///|
pub fn expect_path(path : String, allowed : Bool) -> PathExpectation {
  { path, allowed }
}

///|
pub fn scenario(
  name : String,
  robots : String,
  agent : String,
  expectations : Array[PathExpectation],
) -> Scenario {
  { name, robots, agent, expectations }
}

///|
pub fn run_scenario(case : Scenario) -> Array[String] {
  let failures : Array[String] = []
  let robots = parse(case.robots)
  for item in case.expectations {
    let actual = decide(robots, case.agent, item.path).allowed
    if actual != item.allowed {
      failures.push(
        case.name +
        " " +
        item.path +
        " expected " +
        bool_word(item.allowed) +
        " got " +
        bool_word(actual),
      )
    }
  }
  failures
}

///|
pub fn scenario_report(case : Scenario) -> String {
  let failures = run_scenario(case)
  if failures.is_empty() {
    case.name + ": ok"
  } else {
    join_lines(failures)
  }
}

///|
pub fn run_scenarios(cases : Array[Scenario]) -> Array[String] {
  let failures : Array[String] = []
  for case in cases {
    for failure in run_scenario(case) {
      failures.push(failure)
    }
  }
  failures
}

///|
pub fn scenarios_report(cases : Array[Scenario]) -> String {
  let failures = run_scenarios(cases)
  if failures.is_empty() {
    "ok"
  } else {
    join_lines(failures)
  }
}

///|
pub fn scenario_matrix(case : Scenario) -> String {
  let lines : Array[String] = ["path,expected,actual"]
  let robots = parse(case.robots)
  for item in case.expectations {
    lines.push(
      item.path +
      "," +
      bool_word(item.allowed) +
      "," +
      bool_word(decide(robots, case.agent, item.path).allowed),
    )
  }
  join_lines(lines)
}

///|
pub fn built_in_scenarios() -> Array[Scenario] {
  [
    scenario(
      "private-with-public-hole",
      "User-agent: *\nDisallow: /private\nAllow: /private/public\n",
      "bot",
      [
        expect_path("/", true),
        expect_path("/private/a", false),
        expect_path("/private/public/a", true),
      ],
    ),
    scenario(
      "specific-agent",
      "User-agent: *\nDisallow: /\n\nUser-agent: goodbot\nAllow: /\n",
      "goodbot",
      [expect_path("/", true), expect_path("/anything", true)],
    ),
  ]
}

///|
pub fn built_in_scenarios_report() -> String {
  scenarios_report(built_in_scenarios())
}

///|
pub fn scenario_markdown(case : Scenario) -> String {
  "# Scenario: " +
  case.name +
  "\n\n" +
  fenced(scenario_matrix(case)) +
  "\n\n" +
  fenced(scenario_report(case))
}