use alloc::vec::Vec;
use luaur_ast::records::ast_stat::AstStat;
use luaur_ast::records::ast_stat_block::AstStatBlock;
use luaur_ast::records::ast_stat_for::AstStatFor;
use luaur_ast::records::ast_stat_for_in::AstStatForIn;
use luaur_ast::records::ast_stat_function::AstStatFunction;
use luaur_ast::records::ast_stat_if::AstStatIf;
use luaur_ast::records::ast_stat_local_function::AstStatLocalFunction;
use luaur_ast::records::ast_stat_repeat::AstStatRepeat;
use luaur_ast::records::ast_stat_while::AstStatWhile;
use luaur_ast::rtti::ast_node_as;
use luaur_common::macros::luau_assert::LUAU_ASSERT;

use crate::records::reducer::Reducer;

impl Reducer {
    pub fn get_nested_stats(&self, stat: *mut AstStat) -> Vec<*mut AstStat> {
        let mut result: Vec<*mut AstStat> = Vec::new();

        let mut append = |block: *mut AstStatBlock| {
            if !block.is_null() {
                unsafe {
                    let data = (*block).body.begin();
                    let end = (*block).body.end();
                    let size =
                        (end as usize - data as usize) / core::mem::size_of::<*mut AstStat>();
                    if !data.is_null() && size > 0 {
                        let slice = core::slice::from_raw_parts(data, size);
                        result.extend_from_slice(slice);
                    }
                }
            }
        };

        unsafe {
            if stat.is_null() {
                return result;
            }

            let node_ptr = stat as *mut luaur_ast::records::ast_node::AstNode;

            let block = ast_node_as::<AstStatBlock>(node_ptr);
            if !block.is_null() {
                append(block);
            } else {
                let ifs = ast_node_as::<AstStatIf>(node_ptr);
                if !ifs.is_null() {
                    append((*ifs).thenbody);
                    if !(*ifs).elsebody.is_null() {
                        let else_ptr =
                            (*ifs).elsebody as *mut luaur_ast::records::ast_node::AstNode;
                        let else_block = ast_node_as::<AstStatBlock>(else_ptr);
                        if !else_block.is_null() {
                            append(else_block);
                        } else {
                            let else_if = ast_node_as::<AstStatIf>(else_ptr);
                            if !else_if.is_null() {
                                let inner_stats = self.get_nested_stats(else_if as *mut AstStat);
                                result.extend(inner_stats);
                            } else {
                                eprintln!("AstStatIf's else clause can have more statement types than I thought");
                                LUAU_ASSERT!(false);
                            }
                        }
                    }
                } else {
                    let w = ast_node_as::<AstStatWhile>(node_ptr);
                    if !w.is_null() {
                        append((*w).body);
                    } else {
                        let r = ast_node_as::<AstStatRepeat>(node_ptr);
                        if !r.is_null() {
                            append((*r).body);
                        } else {
                            let f = ast_node_as::<AstStatFor>(node_ptr);
                            if !f.is_null() {
                                append((*f).body);
                            } else {
                                let f_in = ast_node_as::<AstStatForIn>(node_ptr);
                                if !f_in.is_null() {
                                    append((*f_in).body);
                                } else {
                                    let f_func = ast_node_as::<AstStatFunction>(node_ptr);
                                    if !f_func.is_null() {
                                        append((*(*f_func).func).body);
                                    } else {
                                        let f_local = ast_node_as::<AstStatLocalFunction>(node_ptr);
                                        if !f_local.is_null() {
                                            append((*(*f_local).func).body);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        result
    }
}

pub fn reducer_get_nested_stats(this: &Reducer, stat: *mut AstStat) -> Vec<*mut AstStat> {
    this.get_nested_stats(stat)
}
