#[cfg(test)]
#[allow(dead_code)]
pub mod test_utils {
  use chrono::DateTime;
  use ratatui::Frame;
  use ratatui::Terminal;
  use ratatui::backend::TestBackend;
  use ratatui::buffer::Buffer;
  use std::cell::Cell;

  use crate::app::App;

  thread_local! {
    static TIMESTAMP: Cell<i64> = const { Cell::new(0) };
  }

  pub struct Utc;

  impl Utc {
    pub fn now() -> DateTime<chrono::Utc> {
      TIMESTAMP
        .with(|timestamp| {
          let ts = timestamp.get();
          DateTime::<chrono::Utc>::from_timestamp(if ts != 0 { ts } else { 1684618200 }, 0)
        })
        .expect("a valid timestamp set")
    }
  }

  pub fn set_timestamp(timestamp: i64) {
    TIMESTAMP.set(timestamp / 1000);
  }

  pub fn create_test_backend(width: u16, height: u16) -> TestBackend {
    TestBackend::new(width, height)
  }

  pub fn create_test_terminal(width: u16, height: u16) -> Terminal<TestBackend> {
    let backend = create_test_backend(width, height);
    Terminal::new(backend).unwrap()
  }

  pub enum TerminalSize {
    Small,
    Medium,
    Large,
  }

  impl TerminalSize {
    pub fn to_cartesian(&self) -> (u16, u16) {
      match self {
        Self::Small => (80, 30),
        Self::Medium => (120, 40),
        Self::Large => (165, 50),
      }
    }
  }

  /// Renders a UI component and returns the output as a string
  ///
  /// # Arguments
  /// * `size` - Terminal T-shirt size (Small, Medium, or Large)
  /// * `render_fn` - Function that renders to the frame
  ///
  /// # Example
  /// ```rust
  /// let output = render_to_string(TerminalSize::Medium, |f| {
  ///   Block::default().title("Test").render(f.area(), f.buffer_mut());
  /// });
  /// ```
  pub fn render_to_string<F>(size: TerminalSize, mut render_fn: F) -> String
  where
    F: FnMut(&mut Frame),
  {
    let (width, height) = size.to_cartesian();
    let mut terminal = create_test_terminal(width, height);

    terminal
      .draw(|f| {
        render_fn(f);
      })
      .unwrap();

    buffer_to_string(terminal.backend().buffer(), width, height)
  }

  /// Renders a UI component with an App instance and returns the output as a string
  ///
  /// This is the primary helper for testing UI components that need app state.
  ///
  /// # Arguments
  /// * `size` - Terminal T-shirt size (Small, Medium, or Large)
  /// * `app` - Mutable reference to App instance
  /// * `render_fn` - Function that renders to the frame with app
  ///
  /// # Example
  /// ```rust
  /// let mut app = App::test_default();
  /// app.push_navigation_stack(ActiveRadarrBlock::Movies.into());
  ///
  /// let output = render_to_string_with_app(TerminalSize::Medium, &mut app, |f, app| {
  ///   LibraryUi::draw(f, app, f.area());
  /// });
  ///
  /// insta::assert_snapshot!(output);
  /// ```
  pub fn render_to_string_with_app<F>(size: TerminalSize, app: &mut App, mut render_fn: F) -> String
  where
    F: FnMut(&mut Frame, &mut App),
  {
    let (width, height) = size.to_cartesian();
    let mut terminal = create_test_terminal(width, height);

    terminal
      .draw(|f| {
        render_fn(f, app);
      })
      .unwrap();

    buffer_to_string(terminal.backend().buffer(), width, height)
  }

  fn buffer_to_string(buffer: &Buffer, width: u16, height: u16) -> String {
    let mut result = String::new();

    for y in 0..height {
      for x in 0..width {
        let cell = buffer.cell((x, y)).expect("Cell should exist");
        result.push_str(cell.symbol());
      }
      if y < height - 1 {
        result.push('\n');
      }
    }

    result
  }

  pub fn output_contains(output: &str, text: &str) -> bool {
    output.contains(text)
  }

  pub fn output_contains_all(output: &str, texts: &[&str]) -> bool {
    texts.iter().all(|text| output.contains(text))
  }

  pub fn count_lines(output: &str) -> usize {
    output.lines().count()
  }

  pub fn verify_dimensions(output: &str, max_width: usize, max_height: usize) -> bool {
    let lines: Vec<&str> = output.lines().collect();

    if lines.len() > max_height {
      return false;
    }

    lines.iter().all(|line| line.chars().count() <= max_width)
  }
}
