use std::io::Write;

use crossterm::{
  queue,
  style::{Color, Print, ResetColor, SetForegroundColor},
};

use crate::prompt::text_entry;
use crate::{Component, Renderer};

/// Single-column heavy horizontal segment (often called "heavy minus").
const BAR_CHAR: char = '\u{2501}';

/// Countdown strip aligned with prompt text-entry width (`text_entry(..).inner_width`).
#[derive(Debug, Clone)]
pub struct TimerBarComponent {
  pub progress: f64,
  pub visible: bool,
}

impl TimerBarComponent {
  pub fn new() -> Self {
    Self {
      progress: 1.0,
      visible: false,
    }
  }

  pub fn hidden() -> Self {
    Self::new()
  }

  /// Visible segment count when `progress` is in \[0, 1\] and terminal width `terminal_width`.
  pub fn segment_count(progress: f64, terminal_width: u16) -> usize {
    let inner = text_entry("", "", terminal_width).inner_width;
    visible_segments(inner, progress)
  }
}

#[cfg(not(tarpaulin_include))]
impl Component for TimerBarComponent {
  fn render<W: Write>(&self, renderer: &mut Renderer<W>) -> std::io::Result<u16> {
    if !self.visible || renderer.panel().width == 0 {
      return Ok(0);
    }
    renderer.with_panel(|writer, panel, _rng| {
      let body: String = BAR_CHAR.to_string().repeat(panel.width as usize);
      queue!(
        writer,
        SetForegroundColor(Color::DarkGrey),
        Print(body),
        ResetColor
      )?;
      Ok(1)
    })
  }
}

fn visible_segments(inner: u16, progress: f64) -> usize {
  let p = progress.clamp(0.0, 1.0);
  let n = ((inner as f64) * p).floor() as usize;
  n.min(inner as usize)
}

impl Default for TimerBarComponent {
  fn default() -> Self {
    Self::new()
  }
}

#[cfg(test)]
mod tests {
  use super::*;
  use crate::LayoutPanel;

  #[test]
  fn new_is_hidden_with_full_progress() {
    let t = TimerBarComponent::new();
    assert!(!t.visible);
    assert_eq!(t.progress, 1.0);
  }

  #[test]
  fn segment_count_scales_with_progress() {
    let terminal_width = 80_u16;
    let inner = text_entry("", "", terminal_width).inner_width as usize;
    assert_eq!(TimerBarComponent::segment_count(0.0, terminal_width), 0);
    assert_eq!(TimerBarComponent::segment_count(1.0, terminal_width), inner);
    let half = TimerBarComponent::segment_count(0.5, terminal_width);
    assert!(half > 0 && half < inner);
  }

  #[test]
  fn segment_count_clamps_progress() {
    let terminal_width = 100_u16;
    let inner = text_entry("", "", terminal_width).inner_width as usize;
    assert_eq!(TimerBarComponent::segment_count(-1.0, terminal_width), 0);
    assert_eq!(TimerBarComponent::segment_count(2.0, terminal_width), inner);
  }

  use crate::test_helpers::test_renderer;

  #[test]
  fn render_skips_when_not_visible() {
    let t = TimerBarComponent {
      progress: 1.0,
      visible: false,
    };
    let panel = LayoutPanel::centered(80, 10, 5);
    let mut renderer = test_renderer();
    renderer.draw(&t, panel).unwrap();
    assert!(renderer.writer.is_empty());
  }

  #[test]
  fn render_skips_when_zero_width() {
    let t = TimerBarComponent {
      progress: 0.0,
      visible: true,
    };
    let panel = LayoutPanel {
      row: 5,
      column: 0,
      width: 0,
    };
    let mut renderer = test_renderer();
    renderer.draw(&t, panel).unwrap();
    assert!(renderer.writer.is_empty());
  }

  #[test]
  fn hidden_is_alias_for_new() {
    let t = TimerBarComponent::hidden();
    assert!(!t.visible);
    assert_eq!(t.progress, 1.0);
  }

  #[test]
  fn default_matches_new() {
    let t = TimerBarComponent::default();
    assert!(!t.visible);
    assert_eq!(t.progress, 1.0);
  }

  #[test]
  fn render_writes_bar_when_visible() {
    let n = TimerBarComponent::segment_count(1.0, 80);
    let t = TimerBarComponent {
      progress: 1.0,
      visible: true,
    };
    let panel = LayoutPanel::centered(80, n as u16, 3);
    let mut renderer = test_renderer();
    renderer.draw(&t, panel).unwrap();
    let s = String::from_utf8(renderer.writer).unwrap();
    assert_eq!(s.matches(BAR_CHAR).count(), n);
  }
}
