use std::{
    cell::OnceCell,
    marker::PhantomData,
    rc::{Rc, Weak},
};

use crate::{
    combine::{
        expected::Expected,
        parser::{DynParser, Node, Parser},
        result::ParseResult,
    },
    lexer::{
        Lexer,
        token::{Keyword, Token},
    },
};

struct IndirectInner<Kw: Keyword, I: Node> {
    parser: OnceCell<Box<dyn DynParser<Kw, Item = I>>>,
}

enum RecursiveInner<Kw: Keyword, I: Node> {
    Owned(Rc<IndirectInner<Kw, I>>),
    Unowned(Weak<IndirectInner<Kw, I>>),
}

pub struct Recursive<Kw: Keyword, I: Node> {
    inner: RecursiveInner<Kw, I>,
    // Cached per-instance Expected that is itself deferred.
    expected: OnceCell<Expected<Kw>>,
}

pub struct RecursiveHandle<Kw: Keyword, I: Node> {
    inner: Rc<IndirectInner<Kw, I>>,
    marker: PhantomData<Kw>,
}

impl<Kw: Keyword, I: Node> Recursive<Kw, I> {
    fn get_inner(&self) -> Rc<IndirectInner<Kw, I>> {
        match &self.inner {
            RecursiveInner::Owned(rc) => rc.clone(),
            RecursiveInner::Unowned(weak) => weak
                .upgrade()
                .expect("recursive parser used before define() was called"),
        }
    }

    pub fn declare() -> (Self, RecursiveHandle<Kw, I>) {
        let rc = Rc::new(IndirectInner {
            parser: OnceCell::new(),
        });

        let parser = Self {
            inner: RecursiveInner::Owned(rc.clone()),
            expected: OnceCell::new(),
        };

        let handle = RecursiveHandle {
            inner: rc,
            marker: PhantomData,
        };

        (parser, handle)
    }
}

impl<Kw: Keyword, I: Node> Clone for Recursive<Kw, I> {
    fn clone(&self) -> Self {
        Self {
            inner: match &self.inner {
                RecursiveInner::Owned(rc) => RecursiveInner::Unowned(Rc::downgrade(rc)),
                RecursiveInner::Unowned(weak) => RecursiveInner::Unowned(weak.clone()),
            },
            // Each clone gets its own cached Expected cell.
            expected: OnceCell::new(),
        }
    }
}

impl<Kw: Keyword, I: Node + 'static> RecursiveHandle<Kw, I> {
    pub fn define<P: Parser<Kw, Item = I> + 'static>(self, parser: P) {
        self.inner
            .parser
            .set(Box::new(parser))
            .unwrap_or_else(|_| panic!("recursive parser defined twice"));
    }
}

impl<Kw: Keyword + 'static, I: Node + 'static> Parser<Kw> for Recursive<Kw, I> {
    type Item = I;

    fn parse<'lex, M: Fn(&mut Lexer<'lex, Kw>) -> bool>(
        &self,
        lexer: &mut Lexer<'lex, Kw>,
        should_stop: M,
    ) -> ParseResult<Self::Item> {
        self.get_inner()
            .parser
            .get()
            .expect("recursive parser used before define() was called")
            .parse_dyn(lexer, &should_stop)
    }

    fn can_parse(&self, peek: &Token<Kw>) -> bool {
        self.get_inner()
            .parser
            .get()
            .map(|p| p.can_parse(peek))
            .unwrap_or(false)
    }

    fn expected(&self) -> &Expected<Kw> {
        self.expected.get_or_init(|| {
            let inner = self.get_inner();
            Expected::defered(move || {
                inner
                    .parser
                    .get()
                    .expect("recursive parser used before define() was called")
                    .expected()
                    .clone()
            })
        })
    }

    fn is_trivially_skippable() -> bool {
        false
    }
}

pub fn recursive<Kw, I, P, F>(f: F) -> Recursive<Kw, I>
where
    Kw: Keyword,
    I: Node + 'static,
    P: Parser<Kw, Item = I> + 'static,
    F: FnOnce(Recursive<Kw, I>) -> P,
{
    let (parser, handle) = Recursive::declare();
    let definition = f(parser.clone());
    handle.define(definition);
    parser
}

#[macro_export]
macro_rules! recursive {
    (|$name:ident| $body:expr) => {
        $crate::combine::impls::recursive::recursive(|$name| $body)
    };
}
