// bibiman - a TUI for managing BibLaTeX databases
// Copyright (C) 2025  lukeflo
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
/////

use phf::phf_map;
use std::collections::HashMap;

use logos::Logos;

static LOOKUP: phf::Map<&'static str, (&'static str, Option<&'static str>)> = phf_map! {
    r"\mkbibquote" => ("\"", Some("\"")),
    r"\enquote*" => ("\'", Some("\'")),
    r"\enquote" => ("\"", Some("\"")),
    r"\hyphen" => ("-", None),
    r"\textbf" => ("", Some("")),
    r"\textit" => ("", Some("")),
    r"\texttt" => ("", Some("")),
    r"\textsc" => ("", Some("")),
};

static LOOKUP_CLEAR_ALL: phf::Map<&'static str, (&'static str, Option<&'static str>)> = phf_map! {
    r"\mkbibquote" => ("", Some("")),
    r"\enquote*" => ("", Some("")),
    r"\enquote" => ("", Some("")),
    r"\hyphen" => ("", None),
    r"\textbf" => ("", Some("")),
    r"\textit" => ("", Some("")),
    r"\texttt" => ("", Some("")),
    r"\textsc" => ("", Some("")),
};

#[derive(Logos, Debug)]
enum Token {
    #[token("{")]
    OpenCurlyBracket,
    #[token("}")]
    ClosedCurlyBracket,
    #[regex(r"\\[\*\w]+ ?")]
    LaTeXMacro,
    #[token(r"\ ")]
    ForcedSpace,
}

pub fn optimized_sanitize(clear_all: bool, input_text: &str) -> String {
    let lookup = if clear_all {
        &LOOKUP_CLEAR_ALL
    } else {
        &LOOKUP
    };
    let mut char_counter: usize = 0;
    let mut contains_macro: bool = false;
    for char in input_text.chars() {
        if char == '\\' {
            contains_macro = true;
        }
        char_counter = char_counter.saturating_add(1);
    }
    if !contains_macro {
        input_text.to_string()
    } else {
        let mut out: Vec<&str> = Vec::with_capacity(char_counter);
        let mut bracket_counter: u32 = 0;
        let mut bc_up: bool = false;
        let mut counter_actions: HashMap<u32, &str> = HashMap::new();
        let mut lex = Token::lexer(input_text);
        while let Some(sometoken) = lex.next() {
            match sometoken {
                Ok(token) => match token {
                    Token::ForcedSpace => {
                        out.push(" ");
                        bc_up = false;
                    }
                    Token::OpenCurlyBracket => {
                        if bc_up {
                            bracket_counter = bracket_counter.saturating_add(1);
                        } else {
                            out.push("{")
                        }
                    }
                    Token::ClosedCurlyBracket => {
                        if bracket_counter == 0 {
                            out.push("}")
                        } else {
                            match counter_actions.remove(&bracket_counter) {
                                None => out.push("}"),
                                Some(a) => out.push(a),
                            }
                            bracket_counter = bracket_counter - 1;
                        }
                    }
                    Token::LaTeXMacro => {
                        let texmacro = lex.slice();
                        if let Some(x) = lookup.get(&texmacro.trim_end()) {
                            if let Some(end) = x.1 {
                                bc_up = true;
                                counter_actions.insert(bracket_counter + 1, end);
                            }
                            out.push(x.0);
                        } else {
                            out.push(texmacro)
                        }
                    }
                },
                Err(_) => {
                    out.push(lex.slice());
                    bc_up = false;
                }
            }
        }
        out.into_iter().collect::<String>()
    }
}

#[cfg(test)]
mod tests {
    use super::optimized_sanitize;

    #[test]
    fn check_sanitization() {
        let result = optimized_sanitize(
            false,
            r"\mkbibquote {Intention} und \mkbibquote{Intentionen \mkbibquote{sind} \hyphen\ bibquote\hyphen .}",
        );
        assert_eq!(
            "\"Intention\" und \"Intentionen \"sind\" - bibquote-.\"",
            result
        );
        let result = optimized_sanitize(
            true,
            r"\mkbibquote {Intention} und \mkbibquote{Intentionen \mkbibquote{sind} \hyphen\ bibquote\hyphen .}",
        );
        assert_eq!("Intention und Intentionen sind  bibquote.", result)
    }
}
