use rand::rngs::ThreadRng;

use crate::wizard::state::{StateResult, WizardAction, WizardState};
use crate::wizard::ui_controller::UiController;
use crate::wizard::{WizardStep, next_step_index};

use super::exit::Exit;
use super::transition::Transition;

pub struct Input {
  steps: Vec<WizardStep>,
  step_idx: usize,
}

impl Input {
  pub fn new(steps: Vec<WizardStep>, step_idx: usize) -> Self {
    Self { steps, step_idx }
  }
}

impl WizardState for Input {
  fn tick(&mut self, _ui: &mut UiController, _rng: &mut ThreadRng) -> StateResult {
    StateResult::Continue
  }

  fn handle_action(&mut self, action: WizardAction, _ui: &mut UiController) -> StateResult {
    match action {
      WizardAction::Exit => StateResult::Finished,
      WizardAction::Submit(text) => handle_submit(self, &text),
      WizardAction::Navigate(_) | WizardAction::Resize(_, _) => StateResult::Continue,
    }
  }
}

fn handle_submit(state: &mut Input, input_text: &str) -> StateResult {
  let step = &state.steps[state.step_idx];
  let output = build_output(step, input_text);

  let next_idx = next_step_index(state.step_idx, &state.steps, &output);
  let next: Box<dyn WizardState> = match next_idx {
    Some(idx) => Box::new(Transition::new(
      state.steps.clone(),
      state.step_idx,
      idx,
      input_text.to_string(),
    )),
    None => Box::new(Exit::new(
      state.steps.clone(),
      state.step_idx,
      input_text.to_string(),
    )),
  };

  StateResult::Done { next, output }
}

fn build_output(step: &WizardStep, input_text: &str) -> Option<(String, String)> {
  step.env_var().map(|key| {
    let value = if input_text.is_empty() {
      step.default_value().to_string()
    } else {
      input_text.to_string()
    };
    (key.to_string(), value)
  })
}
