pub(crate) mod cache;
pub mod header;

use std::{
    collections::{BTreeMap, HashMap},
    fmt::Debug,
    mem::transmute,
    ops::{Deref, Range},
    sync::Arc,
};

use allocator_api2::vec::Vec;
use bumpalo::Bump;
use tyml_diagnostic::DiagnosticBuilder;
use tyml_formatter::{FormatterToken, FormatterTokenKind, SpaceFormat};
use tyml_generator::{
    lexer::{GeneratorLexer, GeneratorTokenKind, TokenizerRegistry},
    style::{
        error::GeneratedParseError, language::LanguageStyle, ASTTokenKind, Parser, ParserGenerator,
        AST,
    },
};
use tyml_parser::{ast::Defines, error::ParseError, lexer::Lexer, parser::parse_defines};
use tyml_source::SourceCode;
use tyml_type::{
    error::TypeError,
    resolver::resolve_type,
    types::{InterfaceInfo, NamedTypeMap, TypeTree},
};
use tyml_validate::{error::TymlValueValidateError, validate::ValueTypeChecker};

pub extern crate tyml_diagnostic;
pub extern crate tyml_formatter;
pub extern crate tyml_generator;
pub extern crate tyml_parser;
pub extern crate tyml_source;
pub extern crate tyml_type;
pub extern crate tyml_validate;

#[derive(Debug, Clone)]
pub struct TymlContext<State = Initial> {
    state: State,
    pub tyml_source: SourceCode,
}

impl TymlContext {
    pub fn new(tyml_source: SourceCode) -> Self {
        TymlContext {
            state: Initial {},
            tyml_source,
        }
    }
}

impl<State> TymlContext<State> {
    pub fn parse(&self) -> TymlContext<Parsed>
    where
        State: IInitial,
    {
        TymlContext {
            state: Parsed {
                tyml: Tyml::parse(self.tyml_source.code.clone()),
            },
            tyml_source: self.tyml_source.clone(),
        }
    }

    pub fn tyml(&self) -> &Tyml
    where
        State: IParsed,
    {
        self.state.tyml()
    }

    pub fn print_tyml_error(&self, lang: &str)
    where
        State: IParsed,
    {
        for error in self.tyml().parse_errors().iter() {
            error.build(self.tyml().named_type_map()).print(
                lang,
                &self.tyml_source,
                &self.tyml_source,
            );
        }

        for error in self.tyml().type_errors().iter() {
            error.build(self.tyml().named_type_map()).print(
                lang,
                &self.tyml_source,
                &self.tyml_source,
            );
        }
    }

    pub fn has_tyml_error(&self) -> bool
    where
        State: IParsed,
    {
        self.tyml().has_error()
    }

    pub fn ml_parse_and_validate<'code>(
        &self,
        ml_language_style: &LanguageStyle,
        ml_source_code: &'code SourceCode,
        tokens: Option<&mut BTreeMap<usize, (ASTTokenKind, Range<usize>)>>,
        formatter_tokens: Option<&mut std::vec::Vec<FormatterToken<'code>>>,
    ) -> TymlContext<Validated>
    where
        State: IParsed,
    {
        let mut registry = TokenizerRegistry::new();

        let ml_parser = ml_language_style.generate(&mut registry);

        registry.freeze();

        let mut errors = Vec::new();
        let allocator = Bump::new();

        let mut lexer = GeneratorLexer::new(&ml_source_code.code, &registry, &allocator);

        let ast = ml_parser.parse(&mut lexer, &mut errors).unwrap();

        if let Some(formatter_tokens) = formatter_tokens {
            let mut formatter_token_kind_map = HashMap::new();
            let mut formatter_token_space_info = Vec::new();

            ml_parser.map_formatter_token_kind(&mut formatter_token_kind_map);
            ast.take_formatter_token_space(&mut formatter_token_space_info);

            let mut formatter_token_space_info_map = HashMap::new();
            for token_info in formatter_token_space_info {
                if token_info.left_space != SpaceFormat::None {
                    formatter_token_space_info_map
                        .insert(token_info.span.start, token_info.left_space);
                }
                if token_info.right_space != SpaceFormat::None {
                    formatter_token_space_info_map
                        .insert(token_info.span.end, token_info.right_space);
                }
            }

            let lexer = GeneratorLexer::new(&ml_source_code.code, &registry, &allocator);

            for token in lexer {
                let kind = if token.kinds.contains(&GeneratorTokenKind::LineFeed) {
                    FormatterTokenKind::LineFeed
                } else {
                    token
                        .kinds
                        .iter()
                        .find_map(|kind| formatter_token_kind_map.get(kind))
                        .cloned()
                        .unwrap_or(FormatterTokenKind::Normal)
                };

                let left_space = formatter_token_space_info_map
                    .get(&token.span.start)
                    .cloned()
                    .unwrap_or(SpaceFormat::None);

                let right_space = formatter_token_space_info_map
                    .get(&token.span.end)
                    .cloned()
                    .unwrap_or(SpaceFormat::None);

                formatter_tokens.push(FormatterToken {
                    text: token.text.into(),
                    kind,
                    left_space,
                    right_space,
                });
            }
        }

        if let Some(tokens) = tokens {
            ast.take_token(tokens);

            for comment_span in lexer.comment_spans.iter() {
                tokens.insert(
                    comment_span.start,
                    (ASTTokenKind::Comment, comment_span.clone()),
                );
            }
        }

        let mut validator = self.tyml().value_type_checker();

        ast.take_value(&mut Vec::new_in(&allocator), &mut validator);

        let ml_validate_error = validator.validate().err().unwrap_or_default();

        let validator =
            ValidatorHolder::new(self.tyml().clone(), ml_source_code.clone(), validator);

        TymlContext {
            state: Validated {
                tyml: self.tyml().clone(),
                ml_source_code: ml_source_code.clone(),
                validator,
                ml_parse_error: Arc::new(errors),
                ml_validate_error: Arc::new(ml_validate_error),
            },
            tyml_source: self.tyml_source.clone(),
        }
    }

    pub fn ml_source_code(&self) -> &SourceCode
    where
        State: IValidated,
    {
        self.state.ml_source_code()
    }

    pub fn validator(&self) -> &ValueTypeChecker<'_, '_, '_, '_, '_>
    where
        State: IValidated,
    {
        &self.state.validator()
    }

    pub fn print_ml_parse_error(&self, lang: &str)
    where
        State: IValidated,
    {
        for error in self.ml_parse_error().iter() {
            error.build(self.tyml().named_type_map()).print(
                lang,
                &self.tyml_source,
                &self.state.ml_source_code(),
            );
        }
    }

    pub fn has_ml_parse_error(&self) -> bool
    where
        State: IValidated,
    {
        !self.state.ml_parse_error().is_empty()
    }

    pub fn ml_parse_error(&self) -> &Arc<Vec<GeneratedParseError>>
    where
        State: IValidated,
    {
        self.state.ml_parse_error()
    }

    pub fn print_ml_validate_error(&self, lang: &str)
    where
        State: IValidated,
    {
        for error in self.ml_validate_error().iter() {
            error.build(self.tyml().named_type_map()).print(
                lang,
                &self.tyml_source,
                &self.state.ml_source_code(),
            );
        }
    }

    pub fn has_ml_validate_error(&self) -> bool
    where
        State: IValidated,
    {
        !self.state.ml_validate_error().is_empty()
    }

    pub fn ml_validate_error(&self) -> &Arc<Vec<TymlValueValidateError>>
    where
        State: IValidated,
    {
        self.state.ml_validate_error()
    }
}

#[derive(Debug, Clone)]
pub struct Initial {}

#[derive(Debug, Clone)]
pub struct Parsed {
    pub tyml: Tyml,
}

#[derive(Debug, Clone)]
pub struct Validated {
    pub tyml: Tyml,
    pub ml_source_code: SourceCode,
    pub validator: ValidatorHolder,
    pub ml_parse_error: Arc<Vec<GeneratedParseError>>,
    pub ml_validate_error: Arc<Vec<TymlValueValidateError>>,
}

pub trait IInitial {}

impl IInitial for Initial {}

pub trait IParsed {
    fn tyml(&self) -> &Tyml;
}

impl IParsed for Parsed {
    fn tyml(&self) -> &Tyml {
        &self.tyml
    }
}

impl IParsed for Validated {
    fn tyml(&self) -> &Tyml {
        &self.tyml
    }
}

pub trait IValidated: IParsed {
    fn ml_source_code(&self) -> &SourceCode;

    fn validator(&self) -> &ValueTypeChecker<'_, '_, '_, '_, '_>;

    fn ml_parse_error(&self) -> &Arc<Vec<GeneratedParseError>>;

    fn ml_validate_error(&self) -> &Arc<Vec<TymlValueValidateError>>;
}

impl IValidated for Validated {
    fn ml_source_code(&self) -> &SourceCode {
        &self.ml_source_code
    }

    fn validator(&self) -> &ValueTypeChecker<'_, '_, '_, '_, '_> {
        self.validator.validator()
    }

    fn ml_parse_error(&self) -> &Arc<Vec<GeneratedParseError>> {
        &self.ml_parse_error
    }

    fn ml_validate_error(&self) -> &Arc<Vec<TymlValueValidateError>> {
        &self.ml_validate_error
    }
}

/// validator contains part of tyml and ml_source_code
#[derive(Debug, Clone)]
#[allow(unused)]
pub struct ValidatorHolder {
    tyml: Tyml,
    ml_source_code: SourceCode,
    validator: Arc<ValueTypeChecker<'static, 'static, 'static, 'static, 'static>>,
}

impl ValidatorHolder {
    pub fn new(tyml: Tyml, ml_source_code: SourceCode, validator: ValueTypeChecker) -> Self {
        Self {
            tyml,
            ml_source_code,
            validator: unsafe { transmute(Arc::new(validator)) },
        }
    }

    pub fn validator<'this>(
        &'this self,
    ) -> &'this ValueTypeChecker<'this, 'this, 'this, 'this, 'this> {
        // Why dose it need transmute?????
        unsafe { transmute(self.validator.as_ref()) }
    }
}

#[derive(Debug, Clone)]
pub struct Tyml {
    inner: Arc<TymlInner>,
}

impl Tyml {
    pub fn source_code(&self) -> &Arc<String> {
        &self.inner.source_code
    }

    pub fn comment_ranges(&self) -> &Vec<Range<usize>> {
        &self.inner.comments
    }

    pub fn ast<'this>(&'this self) -> &'this Defines<'this, 'this> {
        self.inner.ast
    }

    pub fn type_tree<'this>(&'this self) -> &'this TypeTree<'this> {
        &self.inner.type_tree
    }

    pub fn named_type_map<'this>(&'this self) -> &'this NamedTypeMap<'this> {
        &self.inner.named_type_map
    }

    pub fn parse_errors<'this>(&'this self) -> &'this [ParseError<'this, 'this>] {
        self.inner.parse_errors
    }

    pub fn interfaces<'this>(&'this self) -> &'this [InterfaceInfo<'this, 'this>] {
        self.inner.interfaces
    }

    pub fn type_errors<'this>(&'this self) -> &'this [TypeError<'this>] {
        self.inner.type_errors
    }

    pub fn has_error(&self) -> bool {
        !self.parse_errors().is_empty() || !self.type_errors().is_empty()
    }

    pub fn value_type_checker<'this, 'section, 'value>(
        &'this self,
    ) -> ValueTypeChecker<'this, 'this, 'this, 'section, 'value> {
        ValueTypeChecker::new(self.type_tree(), self.named_type_map())
    }
}

impl Tyml {
    pub fn parse<T: Into<Arc<String>>>(source_code: T) -> Self {
        let source_code = source_code.into();
        let allocator = Box::new(Bump::new());

        let mut lexer = Lexer::new(&source_code);
        let mut parse_errors = Vec::new_in(allocator.deref());

        let ast = parse_defines(&mut lexer, &mut parse_errors, allocator.deref());

        let comments = lexer.comments.into_iter().collect();

        let (type_tree, named_type_map, interfaces, type_errors) =
            resolve_type(ast, allocator.deref());

        let parse_errors = allocator.alloc(parse_errors).as_slice();
        let interfaces = allocator.alloc(interfaces).as_slice();
        let type_errors = allocator.alloc(type_errors).as_slice();

        let fake_static_ast = unsafe { transmute(ast) };
        let fake_static_type_tree = unsafe { transmute(type_tree) };
        let fake_static_named_type_map = unsafe { transmute(named_type_map) };
        let fake_static_parse_errors = unsafe { transmute(parse_errors) };
        let fake_static_interfaces = unsafe { transmute(interfaces) };
        let fake_static_type_errors = unsafe { transmute(type_errors) };

        let _holder = FreezedAllocatorHolder {
            _allocator: allocator,
        };

        let tyml_inner = TymlInner {
            source_code,
            comments,
            ast: fake_static_ast,
            type_tree: fake_static_type_tree,
            named_type_map: fake_static_named_type_map,
            parse_errors: fake_static_parse_errors,
            interfaces: fake_static_interfaces,
            type_errors: fake_static_type_errors,
            _holder,
        };

        Self {
            inner: Arc::new(tyml_inner),
        }
    }
}

/// READ ONLY!!
#[derive(Debug)]
struct TymlInner {
    source_code: Arc<String>,
    comments: Vec<Range<usize>>,
    /// fake static
    ast: &'static Defines<'static, 'static>,
    type_tree: TypeTree<'static>,
    named_type_map: NamedTypeMap<'static>,
    parse_errors: &'static [ParseError<'static, 'static>],
    interfaces: &'static [InterfaceInfo<'static, 'static>],
    type_errors: &'static [TypeError<'static>],
    /// freezed
    _holder: FreezedAllocatorHolder,
}

#[derive(Debug)]
struct FreezedAllocatorHolder {
    _allocator: Box<Bump>,
}

unsafe impl Send for FreezedAllocatorHolder {}
unsafe impl Sync for FreezedAllocatorHolder {}

#[cfg(test)]
mod tests {

    use std::{fs::File, io::Read};

    use tyml_diagnostic::message::Lang;
    use tyml_formatter::GeneralFormatter;
    use tyml_generator::registry::STYLE_REGISTRY;
    use tyml_source::SourceCode;

    use crate::{cache::get_cached_file, TymlContext};

    #[test]
    fn lib_test() {
        let source = r#"
type User {
    id: string | int
    name: string
}

type Claim {
    iss: string
    sub: string
    iat: int
    exp: int
}

/// The API!
interface API {
    #[kind = "get"]
    function register(id: int = 100, name: string = "test") -> string {
        return "* Secret Token *"
    }

    authed function get_user(@claim: Claim) -> User {
        return { id = 100, name = "test" }
    }

    #[kind = "get"]
    function hello() -> string {
        return "Hello, world!"
    }
}

"#;

        let ini_source = r#""#;

        let tyml_source = SourceCode::new("test.tyml".to_string(), source.to_string());
        let ml_source = SourceCode::new("test.json".to_string(), ini_source.to_string());

        let tyml = TymlContext::new(tyml_source).parse();

        let language = STYLE_REGISTRY.resolve("json").unwrap();

        let mut formatter_tokens = Vec::new();

        let tyml =
            tyml.ml_parse_and_validate(&language, &ml_source, None, Some(&mut formatter_tokens));

        tyml.print_tyml_error(Lang::system());
        tyml.print_ml_parse_error(Lang::system());
        tyml.print_ml_validate_error(Lang::system());

        let mut formatter = GeneralFormatter::new(formatter_tokens.into_iter(), 25);
        formatter.format();
        println!("{}", formatter.generate_code());
    }

    #[test]
    fn cookie_resolver_propagation_test() {
        use crate::Tyml;

        let source = r#"
type Token {
    access_token: string
}

interface Auth {
    cookie function refresh() -> Token
    authed cookie function rotate(@claim: Token) -> Token
    function login() -> Token
}
"#;

        let tyml = Tyml::parse(source.to_string());

        assert!(tyml.parse_errors().is_empty(), "{:?}", tyml.parse_errors());
        assert!(tyml.type_errors().is_empty(), "{:?}", tyml.type_errors());

        let interface = tyml
            .interfaces()
            .iter()
            .find(|i| i.original_name == "Auth")
            .expect("Auth interface not found");

        let refresh = interface
            .functions
            .iter()
            .find(|f| f.name.value == "refresh")
            .unwrap();
        assert!(refresh.cookie.is_some(), "refresh must carry cookie span");
        assert!(refresh.authed.is_none());

        let rotate = interface
            .functions
            .iter()
            .find(|f| f.name.value == "rotate")
            .unwrap();
        assert!(rotate.cookie.is_some());
        assert!(rotate.authed.is_some());

        let login = interface
            .functions
            .iter()
            .find(|f| f.name.value == "login")
            .unwrap();
        assert!(login.cookie.is_none());
        assert!(login.authed.is_none());
    }

    #[test]
    fn cache_test() {
        let file_path = tokio::runtime::Runtime::new().unwrap().block_on(async {
            get_cached_file(
                "https://raw.githubusercontent.com/tyml-org/tyml/refs/heads/main/Cargo.toml",
            )
            .await
            .unwrap()
        });
        let mut file = File::open(file_path).unwrap();

        let mut content = String::new();
        file.read_to_string(&mut content).unwrap();

        dbg!(content);
    }
}
