use std::sync::Arc;

use ratatui::prelude::*;
use ratatui::widgets::{Clear, Wrap};
use ratatui::{
  buffer::Buffer,
  layout::Rect,
  symbols::border,
  widgets::{Block, List, Paragraph, Widget},
};

use super::center;

#[derive(Clone, Debug)]
pub enum RenderBuffer {
  List {
    list: Vec<String>,
    index: usize,
    prefix: &'static str,
  },
  String(String),
  Select(Arc<Vec<String>>, usize),
}

#[derive(Debug, PartialEq, Clone)]
pub enum RenderTarget {
  All,
  CliInput,
  CliOutput,
  PopUp
}

#[derive(Debug)]
pub struct Pane {
  pub title: Option<String>,
  pub target: RenderTarget,
  pub buffer: RenderBuffer,
  pub focused: bool,
  pub scroll: i16,
  pub max_scroll: i16,
}

impl Pane {
  pub fn new(
    title: Option<String>,
    target: RenderTarget,
    buffer: RenderBuffer,
    focused: bool,
  ) -> Self {
    Self {
      title,
      target,
      buffer,
      focused,
      scroll: 0,
      max_scroll: 0,
    }
  }
}

impl Widget for &mut Pane {
  fn render(self, area: Rect, buf: &mut Buffer) {
    let block = Block::bordered()
      .title({
        if let Some(t) = &self.title {
          t.clone()
        } else {
          Default::default()
        }
      })
      .border_set(border::PLAIN)
      .border_style({
        if self.focused {
          Style::new().green()
        } else {
          Style::new().white()
        }
      });
    let inner_area = block.inner(area);
    let content_width = inner_area.width as usize;
    let content_height = inner_area.height as usize;
    match &self.buffer {
      RenderBuffer::String(s) => {
        let wrapped_lines = s
          .lines()
          .map(|line| {
            if line.is_empty() {
              1
            } else {
              (line.len() + content_width - 1) / { content_width + (content_width == 0) as usize }
            }
          })
          .sum::<usize>();

        self.max_scroll = if wrapped_lines > content_height {
          (wrapped_lines - content_height) as i16
        } else {
          0
        };

        let scroll_offset = self.max_scroll.saturating_sub(self.scroll) as u16;

        Paragraph::new(s.clone())
          .wrap(Wrap::default())
          .left_aligned()
          .block(block)
          .scroll((scroll_offset as u16, 0))
          .render(area, buf);
      }
      RenderBuffer::Select(list, idx) => {
        let rect = center(area, Constraint::Percentage(60), Constraint::Percentage(60));
        Clear.render(rect, buf);
        self.max_scroll = if list.len() > content_height {
          (list.len() - content_height) as i16
        } else {
          0
        };
        let scroll_offset = self.max_scroll.saturating_sub(self.scroll) as u16;
        let list_w = List::new(
          list
            .iter()
            .skip(scroll_offset as usize)
            .take(content_height)
            .enumerate()
            .map(|(i, s)| {
              Line::from(format!(
                "{}{}",
                "",
                textwrap::fill(s, content_width.saturating_sub(2))
              )).style(if i + scroll_offset as usize == *idx {
                  Style::new().fg(Color::Blue).bg(Color::Green)
                } else {
                  Style::default()
              })
            }),
        )
        .block(block);
        Widget::render(list_w, rect, buf);
      }
      RenderBuffer::List { list, prefix, .. } => {
        self.max_scroll = if list.len() > content_height {
          (list.len() - content_height) as i16
        } else {
          0
        };
        let scroll_offset = self.max_scroll.saturating_sub(self.scroll) as u16;
        let list_w = List::new(
          list
            .iter()
            .skip(scroll_offset as usize)
            .take(content_height)
            .map(|s| {
              format!(
                "{}{}",
                prefix,
                textwrap::fill(s, content_width.saturating_sub(2))
              )
            }),
        )
        .block(block);
        Widget::render(list_w, area, buf);
      }
    }
  }
}
