use std::{any, marker::PhantomData, sync::atomic::AtomicUsize};

use crate::{
    combine::{
        parser::{Node, Parser},
        result::State,
    },
    lexer::{Lexer, span::Spanned, token::Keyword},
};
use rail_diag::Diagnostics;

#[derive(Debug, Clone)]
pub struct Repetition<Kw: Keyword, P: Parser<Kw>> {
    inner: P,
    marker: PhantomData<Kw>,
    num: usize,
}

impl<T: Node> Node for Vec<T> {}

fn join_state(diagnostics: &mut Option<Diagnostics>, other: Diagnostics) {
    match diagnostics {
        Some(curr) => curr.join(other),
        None => {
            *diagnostics = Some(other);
        }
    }
}

#[macro_export]
macro_rules! many {
    ($expr:expr; |$pat:pat_param| $body:expr) => {
        $crate::map!(
            $crate::combine::impls::repeat::Repetition::new($expr.clone()),
            |$pat| $body
        )
    };

    ($expr:expr) => {
        $crate::combine::impls::repeat::Repetition::new($expr.clone())
    };
}

impl<Kw: Keyword, P: Parser<Kw>> Repetition<Kw, P> {
    pub fn new(inner: P) -> Self {
        static COUNT: AtomicUsize = AtomicUsize::new(0);
        Self {
            inner,
            marker: PhantomData,
            num: COUNT.fetch_add(1, std::sync::atomic::Ordering::SeqCst),
        }
    }
}

impl<Kw: Keyword, P: Parser<Kw>> Parser<Kw> for Repetition<Kw, P> {
    type Item = Vec<Spanned<P::Item>>;

    fn parse<'lex, M: Fn(&mut crate::lexer::Lexer<'lex, Kw>) -> bool>(
        &self,
        lexer: &mut crate::lexer::Lexer<'lex, Kw>,
        should_stop: M,
    ) -> crate::combine::result::ParseResult<Self::Item> {
        let should_stop_inner =
            |lexer: &mut Lexer<'lex, Kw>| self.inner.can_parse(&lexer.peek()) || should_stop(lexer);
        let mut items = Vec::new();
        let mut diags = None;
        let mut peek = lexer.peek();
        'outer: loop {
            if !self.inner.can_parse(&peek) {
                let stop = should_stop(lexer);

                if stop {
                    break;
                }

                match self.inner.parse(lexer, should_stop_inner) {
                    Ok(State::Item(item)) => {
                        items.push(item);
                        continue;
                    }
                    Ok(State::Synced(d)) => {
                        join_state(&mut diags, d);
                        continue;
                    }
                    Err(d) => {
                        join_state(&mut diags, d);

                        loop {
                            let next = lexer.peek();
                            if self.inner.can_parse(&next) {
                                break;
                            }

                            if should_stop(lexer) {
                                break 'outer;
                            }
                            lexer.bump();
                        }
                    }
                }
            }

            match self.inner.parse(lexer, should_stop_inner) {
                Ok(State::Item(item)) => {
                    items.push(item);
                }
                Ok(State::Synced(d)) => {
                    join_state(&mut diags, d);
                }
                Err(d) => {
                    join_state(&mut diags, d);

                    let mut next = lexer.peek();
                    loop {
                        if self.inner.can_parse(&next) {
                            break;
                        }

                        if should_stop(lexer) {
                            break 'outer;
                        }
                        next = lexer.bump();
                    }
                }
            }

            peek = lexer.peek();
        }

        match diags {
            Some(d) => Ok(State::Synced(d)),
            None => {
                let span = match items.first() {
                    Some(first) => items
                        .iter()
                        .fold(first.span, |acc, item| acc.union(&item.span)),
                    None => {
                        let at = lexer.last_span().end;
                        (at..at).into()
                    }
                };
                Ok(State::Item(Spanned { value: items, span }))
            }
        }
    }

    fn can_parse(&self, peek: &crate::lexer::token::Token<Kw>) -> bool {
        self.inner.can_parse(peek)
    }

    fn expected(&self) -> &crate::combine::expected::Expected<Kw> {
        self.inner.expected()
    }

    fn is_trivially_skippable() -> bool {
        false
        // P::is_trivially_skippable()
    }

    fn is_optional() -> bool {
        true
    }
}
