use std::{io, rc::Rc};

use console::style;
use indicatif::{MultiProgress, ProgressBar, ProgressFinish, ProgressStyle};

use crate::handlers::{
    Handler, download::SubstituteHandler, global::GlobalHandler,
    substitute_status::CopyPathsHandler,
};

pub struct State {
    pub multi_progress: Rc<MultiProgress>,
    pub handlers: Vec<Box<dyn Handler>>,
    pub term_width: u16,
    pub separator: Option<ProgressBar>,
}

impl State {
    pub fn new(term_width: u16) -> Self {
        let mut state = Self {
            multi_progress: Rc::new(MultiProgress::new()),
            handlers: Vec::new(),
            term_width,
            separator: None,
        };
        state.plug(GlobalHandler);
        state.plug(CopyPathsHandler);
        state.plug(SubstituteHandler);

        state
    }
    pub fn handle(&mut self, action: &crate::action::Action) -> color_eyre::Result<()> {
        let mut prev_handlers = std::mem::take(&mut self.handlers);

        // Check if terminal was resized
        let (_, term_width) = console::Term::stderr().size();

        if term_width != self.term_width {
            self.term_width = term_width;

            for handler in &mut prev_handlers {
                handler.on_resize(self)?;
            }
        }
        if let Some(separator) = &self.separator {
            separator.tick();
        }

        // Applies handles
        let mut retain_result = Ok(());
        prev_handlers.retain_mut(|h| match h.handle(self, action) {
            Ok(x) => x,
            Err(err) => {
                retain_result = Err(err);
                false
            }
        });
        let mut new_handlers = std::mem::replace(&mut self.handlers, prev_handlers);
        self.handlers.append(&mut new_handlers);
        retain_result
    }
    pub fn plug<H: Handler + 'static>(&mut self, handler: H) {
        self.handlers.push(Box::new(handler))
    }

    pub fn add_pb(&mut self, pb: ProgressBar) -> ProgressBar {
        let separator = self.separator.get_or_insert_with(|| {
            let separator = ProgressBar::new_spinner()
                .with_style(
                    ProgressStyle::default_spinner()
                        .template(&style("··{wide_msg:<}").dim().to_string())
                        .expect("invalid template"),
                )
                .with_message("·".repeat(512))
                .with_finish(ProgressFinish::AndClear);

            let separator = self.multi_progress.insert(0, separator);
            separator.set_length(0);
            separator
        });

        self.multi_progress.insert_after(separator, pb)
        // self.progress.add(pb)
    }
    pub fn add_pb_before(&mut self, before: &ProgressBar, pb: ProgressBar) -> ProgressBar {
        self.multi_progress.insert_before(before, pb)
    }
    pub fn add_pb_after(&mut self, after: &ProgressBar, pb: ProgressBar) -> ProgressBar {
        self.multi_progress.insert_after(after, pb)
    }
    pub fn println<I: AsRef<str>>(&self, msg: I) -> io::Result<()> {
        self.multi_progress.println(msg)
    }
}
