use std::rc::Rc;

use crate::vm::value::{
    BooleanValue, ConsCell, IdentifierValue, Keyword, NumberValue, StringValue, Value, Vector,
};

mod binding;
mod call;
mod condition;
mod error;
mod function;
mod loops;
mod macros;

pub use binding::*;
pub use call::*;
pub use condition::*;
pub use error::*;
pub use function::*;
pub use loops::*;
pub use macros::*;

#[derive(Debug, PartialEq)]
pub enum Expression {
    Nil,
    BooleanLiteral(BooleanValue),
    StringLiteral(StringValue),
    IntegerLiteral(NumberValue),
    Identifier(IdentifierValue),
    Lambda(LambdaExpression),
    Defun(DefunExpression),
    Call(CallExpression),
    If(IfExpression),
    Cond(CondExpression),
    Setq(SetqExpression),
    Let(LetExpression),
    Defmacro(DefmacroExpression),
    SyntaxError(ParseError),
    Quote(Rc<Value>),
    While(WhileExpression),
    Loop(LoopExpression),
    Progn(PrognExpression),
    Vector(Rc<Vector>),
    Return(ReturnExpression),
    Break,
    Continue,
}

impl Expression {
    fn map_or<T, F: FnOnce(T) -> Self>(result: Result<T, ParseError>, map: F) -> Rc<Self> {
        match result {
            Ok(r) => Rc::new(map(r)),
            Err(error) => Rc::new(Self::SyntaxError(error)),
        }
    }

    pub fn parse(value: &Value) -> Result<Rc<Self>, Vec<ParseError>> {
        let inner = Self::parse_inner(value);
        let mut errors = vec![];
        if inner.collect_errors(&mut errors) {
            Err(errors)
        } else {
            Ok(inner)
        }
    }

    fn parse_inner(value: &Value) -> Rc<Self> {
        match value {
            Value::Vector(vector) => Rc::new(Self::Vector(vector.clone())),
            Value::HashTable(_) => todo!(),
            Value::String(value) => Rc::new(Self::StringLiteral(value.clone())),
            Value::Quasi(_value) => todo!("{value}"),
            Value::Unquote(_value) => todo!("Unquote {_value}"),
            Value::UnquoteSplice(_value) => todo!("UnquoteSplice {_value}"),
            Value::Quote(value) => Rc::new(Self::Quote(value.clone())),

            Value::Nil => Rc::new(Self::Nil),
            &Value::Number(value) => Rc::new(Self::IntegerLiteral(value)),
            &Value::Boolean(value) => Rc::new(Self::BooleanLiteral(value)),
            Value::Identifier(value) => Rc::new(Self::Identifier(value.clone())),
            Value::Cons(cons) => {
                let ConsCell(car, cdr) = cons.as_ref();
                match car {
                    Value::Keyword(Keyword::Lambda) => {
                        Self::map_or(LambdaExpression::parse(cdr, value), Expression::Lambda)
                    }
                    Value::Keyword(Keyword::If) => {
                        Self::map_or(IfExpression::parse(cdr, value), Expression::If)
                    }
                    Value::Keyword(Keyword::Cond) => {
                        Self::map_or(CondExpression::parse(cdr, value), Expression::Cond)
                    }
                    Value::Keyword(Keyword::Defun) => {
                        Self::map_or(DefunExpression::parse(cdr, value), Expression::Defun)
                    }
                    Value::Keyword(keyword @ (Keyword::Let | Keyword::LetStar)) => {
                        Self::map_or(LetExpression::parse(cdr, value, *keyword), Expression::Let)
                    }
                    Value::Keyword(Keyword::Setq) => {
                        Self::map_or(SetqExpression::parse(cdr, value), Expression::Setq)
                    }
                    Value::Keyword(Keyword::Defmacro) => {
                        Self::map_or(DefmacroExpression::parse(cdr, value), Expression::Defmacro)
                    }
                    Value::Keyword(Keyword::Quote) => {
                        let value = match cdr {
                            Value::Cons(cons) => cons.0.clone(),
                            _ => Value::Nil,
                        };
                        Rc::new(Self::Quote(value.into()))
                    }
                    Value::Keyword(Keyword::Progn) => {
                        Self::map_or(PrognExpression::parse(cdr, value), Expression::Progn)
                    }
                    Value::Keyword(Keyword::Loop) => {
                        Self::map_or(LoopExpression::parse(cdr, value), Expression::Loop)
                    }
                    Value::Keyword(Keyword::While) => {
                        Self::map_or(WhileExpression::parse(cdr, value), Expression::While)
                    }
                    Value::Keyword(Keyword::Break) => Rc::new(Self::Break),
                    Value::Keyword(Keyword::Continue) => Rc::new(Self::Continue),
                    Value::Keyword(Keyword::Return) => {
                        Self::map_or(ReturnExpression::parse(cdr, value), Expression::Return)
                    }
                    _ => Self::map_or(CallExpression::parse(cons, value), Expression::Call),
                }
            }
            Value::Keyword(keyword) => todo!("Error here: keyword {keyword}"),
            Value::Closure(_) => todo!("Error here"),
            Value::Function(_) => todo!("Error here"),
            Value::NativeFunction(_) => todo!("Error here"),
            Value::NativeValue(_) => todo!("Error here"),
        }
    }
}

impl CollectErrors<ParseError> for Expression {
    fn collect_errors(&self, errors: &mut Vec<ParseError>) -> bool {
        match self {
            Self::SyntaxError(error) => {
                errors.push(error.clone());
                true
            }
            Self::If(condition) => condition.collect_errors(errors),
            Self::Cond(condition) => condition.collect_errors(errors),
            Self::Lambda(lambda) => lambda.collect_errors(errors),
            Self::Defun(defun) => defun.collect_errors(errors),
            Self::Call(call) => call.collect_errors(errors),
            Self::Let(let_) => let_.collect_errors(errors),
            Self::Setq(setq) => setq.collect_errors(errors),
            Self::Defmacro(defmacro) => defmacro.collect_errors(errors),
            Self::While(cloop) => cloop.collect_errors(errors),
            Self::Loop(cloop) => cloop.collect_errors(errors),
            Self::Progn(progn) => progn.collect_errors(errors),
            Self::Return(return_) => return_.collect_errors(errors),
            Self::Nil
            | Self::Vector(_)
            | Self::Break
            | Self::Continue
            | Self::IntegerLiteral(_)
            | Self::Identifier(_)
            | Self::BooleanLiteral(_)
            | Self::Quote(_)
            | Self::StringLiteral(_) => false,
        }
    }
}

fn maybe_docstring(input: &Value) -> (&Value, Option<StringValue>) {
    if let Some((Value::String(docstring), cdr)) = input.uncons_ref() {
        (cdr, Some(docstring.clone()))
    } else {
        (input, None)
    }
}

#[cfg(test)]
mod tests {
    use crate::{
        compile::syntax::{ExpectedWhat, ExpectedWhere, Expression, ParseError, ParseErrorKind},
        vm::value::{Keyword, Value},
    };

    #[test]
    fn test_parse_basic() {
        let v = Value::Nil;
        let e = Expression::parse(&v).unwrap();
        assert_eq!(e.as_ref(), &Expression::Nil);

        let v = Value::Number(1234.into());
        let e = Expression::parse(&v).unwrap();
        assert_eq!(e.as_ref(), &Expression::IntegerLiteral(1234.into()));

        let v = Value::Boolean(false.into());
        let e = Expression::parse(&v).unwrap();
        assert_eq!(e.as_ref(), &Expression::BooleanLiteral(false.into()));

        let v = Value::Identifier("a".into());
        let e = Expression::parse(&v).unwrap();
        assert_eq!(e.as_ref(), &Expression::Identifier("a".into()));
    }

    #[test]
    fn test_nested_syntax_error() {
        let inner_if = Value::list_or_nil([Value::Keyword(Keyword::If)]);
        let inner_lambda = Value::list_or_nil([Value::Keyword(Keyword::Lambda)]);
        let outer_if = Value::list_or_nil([
            Value::Keyword(Keyword::If),
            inner_if.clone(),
            inner_lambda.clone(),
            Value::Number(3.into()),
        ]);
        let e = Expression::parse(&outer_if).unwrap_err();
        assert_eq!(
            e,
            vec![
                ParseError {
                    input: inner_if,
                    error: ParseErrorKind::Expected(
                        ExpectedWhat::Condition,
                        ExpectedWhere::AfterKeyword(Keyword::If)
                    )
                },
                ParseError {
                    input: inner_lambda,
                    error: ParseErrorKind::Expected(
                        ExpectedWhat::ArgumentList,
                        ExpectedWhere::AfterKeyword(Keyword::Lambda)
                    )
                }
            ]
        );
    }
}
