use clap::builder;
use owo_colors::{unset_override, OwoColorize};
use rowan::{GreenNode, GreenNodeBuilder, GreenNodeData, GreenTokenData, Language, NodeOrToken};
use std::mem;

use crate::{
    lst_parser::{input::MEANINGLESS_TOKS, syntax_kind::SyntaxKind},
    Lang, SyntaxNode,
};

use super::{
    error::SyntaxError,
    events::{Event, NodeKind},
};

pub struct Output {
    pub green_node: GreenNode,
    pub errors: Vec<SyntaxError>,
}

impl std::fmt::Debug for Output {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let mut errs: Vec<&SyntaxError> = self.errors.iter().collect();
        errs.reverse();

        debug_print_green_node(NodeOrToken::Node(&self.green_node), f, 0, &mut errs, false)
    }
}

const INDENT_STR: &str = "    ";
/// colored argument currently broken
fn debug_print_green_node(
    node: NodeOrToken<&GreenNodeData, &GreenTokenData>,
    f: &mut dyn std::fmt::Write,
    lvl: i32,
    errs: &mut Vec<&SyntaxError>,
    colored: bool,
) -> std::fmt::Result {
    for _ in 0..lvl {
        f.write_str(INDENT_STR)?;
    }

    let r = match node {
        NodeOrToken::Node(n) => {
            let kind = Lang::kind_from_raw(node.kind());
            if kind != SyntaxKind::PARSE_ERR {
                writeln!(
                    f,
                    "{:?} {}",
                    Lang::kind_from_raw(node.kind()).bright_yellow().bold(),
                    "{".yellow()
                )?;
            } else {
                let err = errs
                    .pop()
                    .expect("all error syntax nodes should correspond to an error")
                    .bright_red();

                writeln!(
                    f,
                    "{:?}{} {err:?} {}",
                    kind.bright_red().bold(),
                    ":".red(),
                    "{".bright_red().bold()
                )?;
            }
            for c in n.children() {
                debug_print_green_node(c, f, lvl + 1, errs, colored)?;
            }
            for _ in 0..lvl {
                f.write_str(INDENT_STR)?;
            }
            if kind != SyntaxKind::PARSE_ERR {
                write!(f, "{}", "}\n".yellow())
            } else {
                write!(f, "{}", "}\n".bright_red().bold())
            }
        }
        NodeOrToken::Token(t) => {
            let tok = Lang::kind_from_raw(t.kind());
            if MEANINGLESS_TOKS.contains(tok) {
                writeln!(
                    f,
                    "{:?} {:?}{}",
                    Lang::kind_from_raw(t.kind()).white(),
                    t.text().white(),
                    ";".white()
                )
            } else {
                writeln!(
                    f,
                    "{:?} {:?}{}",
                    Lang::kind_from_raw(t.kind()).bright_cyan().bold(),
                    t.text().green(),
                    ";".yellow()
                )
            }
        }
    };

    r
}

impl Output {
    pub fn debug_colored(&self) -> String {
        let mut out = String::new();
        let mut errs: Vec<&SyntaxError> = self.errors.iter().collect();
        errs.reverse();

        let _ = debug_print_green_node(
            NodeOrToken::Node(&self.green_node),
            &mut out,
            0,
            &mut errs,
            true,
        );

        out
    }
    pub fn from_parser_output(
        mut raw_toks: Vec<(SyntaxKind, &str)>,
        mut events: Vec<Event>,
    ) -> Self {
        let mut builder = GreenNodeBuilder::new();
        let mut fw_parents = Vec::new();
        let mut errors = Vec::new();
        raw_toks.reverse();

        for i in 0..events.len() {
            match mem::replace(&mut events[i], Event::tombstone()) {
                Event::Start {
                    kind,
                    forward_parent,
                } => {
                    if kind == SyntaxKind::TOMBSTONE && forward_parent.is_none() {
                        continue;
                    }

                    fw_parents.push(kind);
                    let mut idx = i;
                    let mut fp = forward_parent;
                    while let Some(fwd) = fp {
                        idx += fwd as usize;
                        fp = match mem::replace(&mut events[idx], Event::tombstone()) {
                            Event::Start {
                                kind,
                                forward_parent,
                            } => {
                                fw_parents.push(kind);
                                forward_parent
                            }
                            _ => unreachable!(),
                        }
                    }

                    // remove whitespace bc it's ugly
                    while let Some((SyntaxKind::WHITESPACE | SyntaxKind::NEWLINE, _)) =
                        raw_toks.last()
                    {
                        match events.iter_mut().find(|ev| matches!(ev, Event::Eat { .. })) {
                            Some(Event::Eat { count }) => *count -= 1,
                            _ => unreachable!(),
                        }

                        let (tok, text): (SyntaxKind, &str) = raw_toks.pop().unwrap();
                        builder.token(tok.into(), text);
                    }

                    for kind in fw_parents.drain(..).rev() {
                        match kind {
                            NodeKind::Syntax(kind) if kind != SyntaxKind::TOMBSTONE => {
                                builder.start_node(kind.into())
                            }
                            NodeKind::Error(err) => {
                                errors.push(err);
                                builder.start_node(SyntaxKind::PARSE_ERR.into())
                            }
                            _ => {}
                        }
                    }
                }
                Event::Finish => builder.finish_node(),
                Event::Eat { count } => (0..count).for_each(|_| {
                    let (tok, text): (SyntaxKind, &str) = raw_toks.pop().unwrap();
                    builder.token(tok.into(), text);
                }),
            }
        }

        Self {
            green_node: builder.finish(),
            errors,
        }
    }

    pub fn syntax(&self) -> SyntaxNode {
        SyntaxNode::new_root(self.green_node.clone())
    }

    pub fn errors(&self) -> Vec<SyntaxError> {
        self.errors.clone()
    }

    pub fn dissolve(self) -> (GreenNode, Vec<SyntaxError>) {
        let Self { green_node, errors } = self;
        (green_node, errors)
    }
}
