use std::vec;

use syn::{punctuated::Punctuated, token, PathSegment};
use typst::syntax::ast::Params;

use crate::literal_string;

pub fn typst_scope_to_rust(scope: &typst::eval::Scope) -> syn::Item {
    let functions = scope
        .iter()
        .map(|(name, value)| {
            if let Ok(value) = value.clone().cast::<typst::eval::Func>() {
                typst_func_to_rust(name.as_str(), value)
            } else {
                todo!()
            }
        })
        .collect::<Vec<_>>();

    syn::Item::Mod(syn::ItemMod {
        attrs: Vec::new(),
        vis: syn::Visibility::Public(token::Pub::default()),
        unsafety: None,
        mod_token: token::Mod::default(),
        ident: syn::Ident::new("test", proc_macro2::Span::call_site()),
        content: Some((token::Brace::default(), functions)),
        semi: None,
    })
}

fn typst_params_to_syn<'a>(params: Params<'a>) -> impl Iterator<Item = syn::FnArg> + 'a {
    dbg!(params.children().collect::<Vec<_>>());
    params.children().map(|child| {
        if let typst::syntax::ast::Param::Pos(typst::syntax::ast::Pattern::Normal(
            typst::syntax::ast::Expr::Ident(param),
        )) = child
        {
            syn::FnArg::Typed(syn::PatType {
                attrs: Vec::new(),
                pat: Box::new(syn::Pat::Ident(syn::PatIdent {
                    attrs: Vec::new(),
                    by_ref: None,
                    mutability: None,
                    ident: syn::Ident::new(param.as_str(), proc_macro2::Span::call_site()),
                    subpat: None,
                })),
                colon_token: token::Colon::default(),
                ty: Box::new(syn::Type::Path(syn::TypePath {
                    qself: None,
                    path: syn::Path {
                        leading_colon: None,
                        segments: Punctuated::from_iter(
                            vec![
                                syn::PathSegment {
                                    ident: syn::Ident::new("typst", proc_macro2::Span::call_site()),
                                    arguments: syn::PathArguments::None,
                                },
                                syn::PathSegment {
                                    ident: syn::Ident::new("eval", proc_macro2::Span::call_site()),
                                    arguments: syn::PathArguments::None,
                                },
                                syn::PathSegment {
                                    ident: syn::Ident::new("Value", proc_macro2::Span::call_site()),
                                    arguments: syn::PathArguments::None,
                                },
                            ]
                            .into_iter(),
                        ),
                    },
                })),
            })
        } else {
            dbg!(child);
            todo!()
        }
    })
}

fn typst_op_to_syn(operation: typst::syntax::ast::BinOp) -> syn::BinOp {
    match operation {
        typst::syntax::ast::BinOp::Add => syn::BinOp::Add(token::Plus::default()),
        _ => todo!(),
    }
}

fn typst_expr_to_syn(expression: typst::syntax::ast::Expr) -> syn::Expr {
    match expression {
        typst::syntax::ast::Expr::Binary(binary) => syn::Expr::Call(syn::ExprCall {
            attrs: Vec::new(),
            func: Box::new(syn::Expr::Path(syn::ExprPath {
                attrs: Vec::new(),
                qself: None,
                path: syn::Path {
                    leading_colon: None,
                    segments: Punctuated::from_iter(
                        vec![
                            syn::PathSegment {
                                ident: syn::Ident::new("typst", proc_macro2::Span::call_site()),
                                arguments: syn::PathArguments::None,
                            },
                            syn::PathSegment {
                                ident: syn::Ident::new("eval", proc_macro2::Span::call_site()),
                                arguments: syn::PathArguments::None,
                            },
                            syn::PathSegment {
                                ident: syn::Ident::new("ops", proc_macro2::Span::call_site()),
                                arguments: syn::PathArguments::None,
                            },
                            syn::PathSegment {
                                ident: syn::Ident::new(
                                    match binary.op() {
                                        typst::syntax::ast::BinOp::Add => "add",
                                        _ => todo!(),
                                    },
                                    proc_macro2::Span::call_site(),
                                ),
                                arguments: syn::PathArguments::None,
                            },
                        ]
                        .into_iter(),
                    ),
                },
            })),
            paren_token: token::Paren::default(),
            args: Punctuated::from_iter(
                vec![
                    typst_expr_to_syn(binary.lhs()),
                    typst_expr_to_syn(binary.rhs()),
                ]
                .into_iter(),
            ),
        }),
        // syn::Expr::Binary(syn::ExprBinary {
        //     attrs: Vec::new(),
        //     left: Box::new(typst_expr_to_syn(binary.lhs())),
        //     op: typst_op_to_syn(binary.op()),
        //     right: Box::new(typst_expr_to_syn(binary.rhs())),
        // }),
        typst::syntax::ast::Expr::Ident(ident) => syn::Expr::Path(syn::ExprPath {
            attrs: Vec::new(),
            qself: None,
            path: syn::Path {
                leading_colon: None,
                segments: Punctuated::from_iter(
                    Some(syn::PathSegment {
                        ident: syn::Ident::new(ident.as_str(), proc_macro2::Span::call_site()),
                        arguments: syn::PathArguments::None,
                    })
                    .into_iter(),
                ),
            },
        }),
        _ => {
            dbg!(expression);
            todo!()
        }
    }
}

pub fn typst_func_to_rust(name: &str, func: typst::eval::Func) -> syn::Item {
    syn::Item::Fn(match func.repr {
        typst::eval::FuncRepr::Closure(closure) => {
            let closure_ast = closure.node.cast::<typst::syntax::ast::Closure>().unwrap();
            dbg!(typst_params_to_syn(closure_ast.params()).collect::<Vec<_>>());
            syn::ItemFn {
                attrs: Vec::new(),
                vis: syn::Visibility::Public(token::Pub::default()),
                sig: syn::Signature {
                    constness: None,
                    asyncness: None,
                    unsafety: None,
                    abi: None,
                    fn_token: token::Fn::default(),
                    ident: syn::Ident::new(name, proc_macro2::Span::call_site()),
                    generics: syn::Generics::default(),
                    paren_token: token::Paren::default(),
                    inputs: Punctuated::from_iter(typst_params_to_syn(closure_ast.params())),
                    variadic: None,
                    output: syn::ReturnType::Type(
                        token::RArrow::default(),
                        Box::new(syn::Type::Path(syn::TypePath {
                            qself: None,
                            path: syn::Path {
                                leading_colon: None,
                                segments: Punctuated::from_iter(
                                    vec![
                                        syn::PathSegment {
                                            ident: syn::Ident::new(
                                                "typst",
                                                proc_macro2::Span::call_site(),
                                            ),
                                            arguments: syn::PathArguments::None,
                                        },
                                        syn::PathSegment {
                                            ident: syn::Ident::new(
                                                "diag",
                                                proc_macro2::Span::call_site(),
                                            ),
                                            arguments: syn::PathArguments::None,
                                        },
                                        syn::PathSegment {
                                            ident: syn::Ident::new(
                                                "StrResult",
                                                proc_macro2::Span::call_site(),
                                            ),
                                            arguments: syn::PathArguments::AngleBracketed(
                                                syn::AngleBracketedGenericArguments {
                                                    colon2_token: None,
                                                    lt_token: token::Lt::default(),
                                                    args: Punctuated::from_iter(
                                                        Some(syn::GenericArgument::Type(
                                                            syn::Type::Path(syn::TypePath {
                                                                qself: None,
                                                                path: syn::Path {
                                                                    leading_colon: None,
                                                                    segments: Punctuated::from_iter(
                                                                        vec![
                                                                            syn::PathSegment {
                                                                                ident: syn::Ident::new(
                                                                                    "typst",
                                                                                    proc_macro2::Span::call_site(),
                                                                                ),
                                                                                arguments: syn::PathArguments::None,
                                                                            },
                                                                            syn::PathSegment {
                                                                                ident: syn::Ident::new(
                                                                                    "eval",
                                                                                    proc_macro2::Span::call_site(),
                                                                                ),
                                                                                arguments: syn::PathArguments::None,
                                                                            },syn::PathSegment {
                                                                                ident: syn::Ident::new(
                                                                                    "Value",
                                                                                    proc_macro2::Span::call_site(),
                                                                                ),
                                                                                arguments: syn::PathArguments::None,
                                                                            }].into_iter(),
                                                                    ),
                                                                },
                                                            }),
                                                        ))
                                                        .into_iter(),
                                                    ),
                                                    gt_token: token::Gt::default(),
                                                },
                                            ),
                                        },
                                    ]
                                    .into_iter(),
                                ),
                            },
                        })),
                    ),
                },
                block: Box::new(syn::Block {
                    brace_token: token::Brace::default(),
                    stmts: vec![syn::Stmt::Expr(typst_expr_to_syn(closure_ast.body()), None)],
                }),
            }
        }
        _ => todo!(),
    })
}