use std::sync::Arc;

use tombi_comment_directive::{
    TOMBI_COMMENT_DIRECTIVE_TOML_VERSION, TombiCommentDirectiveImpl,
    document::TombiDocumentDirectiveContent,
};
use tombi_comment_directive_store::comment_directive_document_schema;
use tombi_diagnostic::SetDiagnostics;
use tombi_document::IntoDocument;
use tombi_document_tree::IntoDocumentTreeAndErrors;

use crate::comment_directive::into_directive_diagnostic;

pub async fn get_tombi_document_comment_directive(
    root: &tombi_ast::Root,
) -> Option<TombiDocumentDirectiveContent> {
    get_tombi_document_comment_directive_and_diagnostics(root)
        .await
        .0
}

pub async fn get_tombi_document_comment_directive_and_diagnostics(
    root: &tombi_ast::Root,
) -> (
    Option<TombiDocumentDirectiveContent>,
    Vec<tombi_diagnostic::Diagnostic>,
) {
    use serde::Deserialize;

    let mut total_document_tree_table: Option<tombi_document_tree::Table> = None;
    let mut total_diagnostics = Vec::new();
    let mut tombi_directive_iter = root.tombi_document_comment_directives().peekable();

    if tombi_directive_iter.peek().is_some() {
        let toml_version = TOMBI_COMMENT_DIRECTIVE_TOML_VERSION;
        let schema_store = tombi_comment_directive_store::schema_store().await;
        let document_schema = comment_directive_document_schema(
            schema_store,
            TombiDocumentDirectiveContent::comment_directive_schema_url(),
        )
        .await;

        let source_schema = tombi_schema_store::SourceSchema::new(
            Some(Arc::new(document_schema)),
            tombi_hashmap::IndexMap::with_capacity(0),
            Some(toml_version),
            None,
            Default::default(),
            Default::default(),
            Default::default(),
        );

        let schema_context = tombi_schema_store::SchemaContext {
            toml_version,
            root_schema: source_schema.root_schema.as_deref(),
            sub_schema_uri_map: None,
            deprecated_lint_level: None,
            schema_format_rules: None,
            schema_lint_rules: None,
            schema_overrides: None,
            schema_visits: Default::default(),
            store: schema_store,
            strict: None,
        };

        for tombi_ast::TombiDocumentCommentDirective {
            content,
            content_range,
            ..
        } in tombi_directive_iter
        {
            let (root, errors) = tombi_parser::parse(&content).into_root_and_errors();
            // Check if there are any parsing errors
            if !errors.is_empty() {
                let mut diagnostics = Vec::new();
                for error in errors {
                    error.set_diagnostics(&mut diagnostics);
                }
                total_diagnostics.extend(
                    diagnostics
                        .into_iter()
                        .map(|diagnostic| into_directive_diagnostic(&diagnostic, content_range)),
                );
                continue;
            }

            let (document_tree, errors) = root
                .into_document_tree_and_errors(TOMBI_COMMENT_DIRECTIVE_TOML_VERSION)
                .into();

            // Check for errors during document tree construction
            if !errors.is_empty() {
                let mut diagnostics = Vec::new();
                for error in errors {
                    error.set_diagnostics(&mut diagnostics);
                }
                total_diagnostics.extend(
                    diagnostics
                        .into_iter()
                        .map(|diagnostic| into_directive_diagnostic(&diagnostic, content_range)),
                );
            } else if let Err(diagnostics) =
                crate::validate(document_tree.clone(), Some(&source_schema), &schema_context).await
            {
                total_diagnostics.extend(
                    diagnostics
                        .into_iter()
                        .map(|diagnostic| into_directive_diagnostic(&diagnostic, content_range)),
                );
            }
            if let Some(total_document_tree_table) = total_document_tree_table.as_mut() {
                if let Err(errors) = total_document_tree_table.merge(document_tree.into()) {
                    let mut diagnostics = Vec::new();
                    for error in errors {
                        error.set_diagnostics(&mut diagnostics);
                    }
                    total_diagnostics.extend(
                        diagnostics.into_iter().map(|diagnostic| {
                            into_directive_diagnostic(&diagnostic, content_range)
                        }),
                    );
                }
            } else {
                total_document_tree_table = Some(document_tree.into());
            }
        }
    }

    if let Some(total_document_tree_table) = total_document_tree_table {
        (
            TombiDocumentDirectiveContent::deserialize(
                &total_document_tree_table.into_document(TOMBI_COMMENT_DIRECTIVE_TOML_VERSION),
            )
            .ok(),
            total_diagnostics,
        )
    } else {
        (None, total_diagnostics)
    }
}
