use std::str;

use anyhow::{Result, bail};
use mdbook_preprocessor::{
    Preprocessor, PreprocessorContext,
    book::{Book, BookItem},
};

use crate::{parser::tabs::parse_tabs, tabs::tabs};

pub struct TabsPreprocessor;

impl TabsPreprocessor {
    pub fn new() -> Self {
        Self
    }
}

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

impl Preprocessor for TabsPreprocessor {
    fn name(&self) -> &str {
        "tabs"
    }

    fn run(&self, _ctx: &PreprocessorContext, book: Book) -> Result<Book> {
        let mut book = book.clone();

        process_items(&mut book.items)?;

        Ok(book)
    }

    fn supports_renderer(&self, _renderer: &str) -> Result<bool> {
        Ok(true)
    }
}

fn process_items(items: &mut Vec<BookItem>) -> Result<()> {
    for section in items {
        if let BookItem::Chapter(chapter) = section {
            loop {
                let (configs, has_nested) = parse_tabs(chapter)?;

                let mut offset: isize = 0;

                for (span, config) in configs {
                    let replacement = tabs(&config);

                    let start = span.start as isize + offset;
                    let end = span.end as isize + offset;
                    if start < 0 || end < 0 {
                        bail!("Negative range {}..{}.", start, end);
                    }

                    chapter
                        .content
                        .replace_range(start as usize..end as usize, &replacement);

                    offset += replacement.len() as isize - span.len() as isize;
                }

                if !has_nested {
                    break;
                }
            }

            process_items(&mut chapter.sub_items)?;
        }
    }

    Ok(())
}
