///|
/// Interactive prompt implementation using async I/O.
/// 
/// This module provides the core prompt function and helper utilities.
/// Note: In MoonBit, async functions are called like regular functions -
/// they automatically wait for completion when called from another async function.
pub async fn prompt(questions : Array[Question]) -> Answer {
  let answers : Answer = {}
  for question in questions {
    let answer_value = ask_question(question)
    answers[question.question_name] = answer_value
  }
  answers
}

///|
async fn ask_question(question : Question) -> AnswerValue {
  match question.question_type {
    QuestionType::Input =>
      read_input(question.question_message, question.question_default)
    QuestionType::Confirm =>
      read_confirm(question.question_message, question.question_default)
    QuestionType::List =>
      read_list(
        question.question_message,
        question.question_choices,
        question.question_default,
      )
    QuestionType::RawList =>
      read_rawlist(
        question.question_message,
        question.question_choices,
        question.question_default,
      )
    QuestionType::Expand =>
      read_expand(question.question_message, question.question_choices)
    QuestionType::Checkbox =>
      read_checkbox(
        question.question_message,
        question.question_choices,
        question.question_default,
      )
    QuestionType::Password => read_password(question.question_message)
    QuestionType::Editor => read_editor(question.question_message)
  }
}

///|
fn string_view_to_string(sv : StringView) -> String {
  sv.to_owned()
}

///|
async fn read_input(message : String, default : AnswerValue?) -> AnswerValue {
  let default_str = match default {
    Some(AnswerValue::StringValue(s)) => s
    _ => ""
  }

  let prompt_str = if default_str.length() > 0 {
    "\{message} (\{default_str}): "
  } else {
    "\{message}: "
  }

  @stdio.stdout.write(prompt_str)
  let input = @stdio.stdin.read_until("\n")
  let input = match input {
    Some(sv) => string_view_to_string(sv.trim())
    None => ""
  }

  if input.length() == 0 {
    AnswerValue::StringValue(default_str)
  } else {
    AnswerValue::StringValue(input)
  }
}

///|
async fn read_confirm(message : String, default : AnswerValue?) -> AnswerValue {
  let default_bool = match default {
    Some(AnswerValue::BoolValue(b)) => b
    _ => true
  }

  let default_str = if default_bool { "[Y/n]" } else { "[y/N]" }
  @stdio.stdout.write("\{message} \{default_str}: ")

  let input = @stdio.stdin.read_until("\n")
  let input = match input {
    Some(sv) => string_view_to_string(sv.trim().to_upper())
    None => ""
  }

  match input {
    "Y" | "YES" | "TRUE" | "1" => AnswerValue::BoolValue(true)
    "N" | "NO" | "FALSE" | "0" => AnswerValue::BoolValue(false)
    _ => AnswerValue::BoolValue(default_bool)
  }
}

///|
async fn read_list(
  message : String,
  choices : Array[String]?,
  default : AnswerValue?,
) -> AnswerValue {
  match choices {
    Some(choices) if choices.length() > 0 => {
      @stdio.stdout.write("\{message}\n")
      for i, choice in choices {
        let default_marker = match default {
          Some(AnswerValue::IntValue(idx)) if idx == i => " (default)"
          _ => ""
        }
        @stdio.stdout.write("  \{i + 1}. \{choice}\{default_marker}\n")
      }

      let red = "\u001b[31m"
      let reset = "\u001b[0m"
      let n = choices.length()
      let mut result = AnswerValue::StringValue(choices[0])
      let mut done = false
      while !done {
        @stdio.stdout.write("Choose an option: ")
        let input = @stdio.stdin.read_until("\n")
        let input = match input {
          Some(sv) => string_view_to_string(sv.trim())
          None => ""
        }

        match safe_parse_int(input) {
          Some(num) if num >= 1 && num <= n => {
            result = AnswerValue::StringValue(choices[num - 1])
            done = true
          }
          Some(num) =>
            @stdio.stdout.write(
              red +
              "Option " +
              num.to_string() +
              " does not exist. Please enter a number between 1 and " +
              n.to_string() +
              ".\n" +
              reset,
            )
          None if input.length() == 0 => {
            let default_idx = match default {
              Some(AnswerValue::IntValue(idx)) if idx >= 0 && idx < n => idx
              _ => 0
            }
            result = AnswerValue::StringValue(choices[default_idx])
            done = true
          }
          _ =>
            @stdio.stdout.write(
              red +
              "'" +
              input +
              "' is not a valid number. Please enter a number between 1 and " +
              n.to_string() +
              ".\n" +
              reset,
            )
        }
      }
      result
    }
    _ => AnswerValue::StringValue("")
  }
}

///|
async fn read_rawlist(
  message : String,
  choices : Array[String]?,
  default : AnswerValue?,
) -> AnswerValue {
  match choices {
    Some(choices) if choices.length() > 0 => {
      @stdio.stdout.write("\{message}\n")

      for i, choice in choices {
        let default_marker = match default {
          Some(AnswerValue::IntValue(idx)) if idx == i => " (default)"
          _ => ""
        }
        @stdio.stdout.write("  \{i + 1}. \{choice}\{default_marker}\n")
      }

      @stdio.stdout.write("Choose an option: ")
      let input = @stdio.stdin.read_until("\n")
      let input = match input {
        Some(sv) => string_view_to_string(sv.trim())
        None => ""
      }

      match safe_parse_int(input) {
        Some(num) if num >= 1 && num <= choices.length() =>
          AnswerValue::StringValue(choices[num - 1])
        _ => {
          let default_idx = match default {
            Some(AnswerValue::IntValue(idx)) if idx >= 0 &&
              idx < choices.length() => idx
            _ => 0
          }
          AnswerValue::StringValue(choices[default_idx])
        }
      }
    }
    _ => AnswerValue::StringValue("")
  }
}

///|
async fn read_expand(message : String, choices : Array[String]?) -> AnswerValue {
  match choices {
    Some(choices) if choices.length() > 0 => {
      @stdio.stdout.write("\{message}\n")

      for i, choice in choices {
        let key = Int::unsafe_to_char(97 + i).to_string() // a, b, c, ...
        @stdio.stdout.write("  [\{key}] \{choice}\n")
      }

      @stdio.stdout.write("Choose an option: ")
      let input = @stdio.stdin.read_until("\n")
      let input = match input {
        Some(sv) => string_view_to_string(sv.trim().to_upper())
        None => ""
      }

      // Find matching key
      for i, _ in choices {
        let key = Int::unsafe_to_char(97 + i).to_string().to_upper()
        if input == key {
          return AnswerValue::StringValue(choices[i])
        }
      }

      // Default to first option
      AnswerValue::StringValue(choices[0])
    }
    _ => AnswerValue::StringValue("")
  }
}

///|
async fn read_checkbox(
  message : String,
  choices : Array[String]?,
  default : AnswerValue?,
) -> AnswerValue {
  match choices {
    Some(choices) if choices.length() > 0 => {
      @stdio.stdout.write("\{message}\n")

      let default_arr = match default {
        Some(AnswerValue::ArrayValue(arr)) => arr
        _ => []
      }

      for i, choice in choices {
        let marker = if default_arr.contains(choice) { "[x]" } else { "[ ]" }
        @stdio.stdout.write("  \{marker} \{i + 1}. \{choice}\n")
      }

      let red = "\u001b[31m"
      let reset = "\u001b[0m"
      let n = choices.length()
      let mut result = AnswerValue::ArrayValue([])
      let mut done = false
      while !done {
        @stdio.stdout.write(
          "\nEnter numbers separated by commas (e.g., 1,3,5), 0 for all, or press Enter for default: ",
        )
        let input = @stdio.stdin.read_until("\n")
        let input = match input {
          Some(sv) => string_view_to_string(sv.trim())
          None => ""
        }

        if input.length() == 0 {
          if default_arr.length() > 0 {
            result = AnswerValue::ArrayValue(default_arr)
          } else {
            result = AnswerValue::ArrayValue(choices)
          }
          done = true
        } else if input == "0" {
          result = AnswerValue::ArrayValue(choices)
          done = true
        } else {
          let selected : Array[String] = []
          let invalid_nums : Array[Int] = []
          let parts = input.split(",")
          for part_sv in parts {
            let part = string_view_to_string(part_sv.trim())
            match safe_parse_int(part) {
              Some(num) if num >= 1 && num <= n =>
                selected.push(choices[num - 1])
              Some(num) => invalid_nums.push(num)
              _ => ignore(())
            }
          }

          if invalid_nums.length() > 0 {
            let invalid_strs : Array[String] = []
            for n in invalid_nums {
              invalid_strs.push(n.to_string())
            }
            @stdio.stdout.write(
              red +
              "Options do not exist: " +
              invalid_strs.join(", ") +
              ". Please enter numbers between 1 and " +
              n.to_string() +
              ".\n" +
              reset,
            )
          } else if selected.length() == 0 {
            @stdio.stdout.write(
              red +
              "No valid options selected. Please enter numbers between 1 and " +
              n.to_string() +
              ".\n" +
              reset,
            )
          } else {
            result = AnswerValue::ArrayValue(selected)
            done = true
          }
        }
      }
      result
    }
    _ => AnswerValue::ArrayValue([])
  }
}

///|
async fn read_password(message : String) -> AnswerValue {
  @stdio.stdout.write("\{message}: ")
  let input = @stdio.stdin.read_until("\n")
  let input = match input {
    Some(sv) => string_view_to_string(sv.trim())
    None => ""
  }
  AnswerValue::StringValue(input)
}

///|
async fn read_editor(message : String) -> AnswerValue {
  @stdio.stdout.write(
    "\{message} (Enter your text, press Enter once to finish):\n",
  )

  // Read single line for editor
  let input = @stdio.stdin.read_until("\n")
  let input = match input {
    Some(sv) => string_view_to_string(sv.trim())
    None => ""
  }
  AnswerValue::StringValue(input)
}

///|
/// Safe wrapper for @string.parse_int that returns Int? instead of raising
pub fn safe_parse_int(s : String) -> Int? {
  Some(@string.parse_int(s)) catch {
    _ => None
  }
}

///|
/// Alias for backwards compatibility
pub fn parse_int(s : String) -> Int? {
  safe_parse_int(s)
}