use tombi_extension::CompletionContentPriority;
use tombi_future::Boxable;
use tombi_schema_store::{
    Accessor, AnyOfSchema, CurrentSchema, ReferableValueSchemas, SchemaAccessor, ValueSchema,
};

use crate::completion::{
    CompletionCandidate, CompletionContent, CompletionHint, CompositeSchemaImpl,
    FindCompletionContents, tombi_json_value_to_completion_default_item,
    tombi_json_value_to_completion_example_item,
};

impl CompositeSchemaImpl for AnyOfSchema {
    fn title(&self) -> Option<String> {
        self.title.clone()
    }

    fn description(&self) -> Option<String> {
        self.description.clone()
    }

    fn schemas(&self) -> &ReferableValueSchemas {
        &self.schemas
    }
}

pub fn find_any_of_completion_items<'a: 'b, 'b, T>(
    value: &'a T,
    position: tombi_text::Position,
    keys: &'a [tombi_document_tree::Key],
    accessors: &'a [Accessor],
    any_of_schema: &'a tombi_schema_store::AnyOfSchema,
    current_schema: &'a CurrentSchema<'a>,
    schema_context: &'a tombi_schema_store::SchemaContext<'a>,
    completion_hint: Option<CompletionHint>,
) -> tombi_future::BoxFuture<'b, Vec<CompletionContent>>
where
    T: FindCompletionContents + tombi_validator::Validate + Sync + Send + std::fmt::Debug,
{
    log::trace!("value = {:?}", value);
    log::trace!("position = {:?}", position);
    log::trace!("keys = {:?}", keys);
    log::trace!("accessors = {:?}", accessors);
    log::trace!("any_of_schema = {:?}", any_of_schema);
    log::trace!("completion_hint = {:?}", completion_hint);

    async move {
        let mut completion_items = Vec::new();

        // Same branching narrow logic as oneOf: when completing the value of a single key, only
        // consider branches that are a table containing that key; skip default/examples when narrow.
        let first_key = (keys.len() == 1 && !keys[0].value.is_empty()).then(|| &keys[0].value);

        let mut branch_results: Vec<(bool, Vec<CompletionContent>)> = Vec::new();
        for referable_schema in any_of_schema.schemas.write().await.iter_mut() {
            let Ok(Some(current_schema)) = referable_schema
                .resolve(
                    current_schema.schema_uri.clone(),
                    current_schema.definitions.clone(),
                    schema_context.store,
                )
                .await
            else {
                continue;
            };

            let branch_has_key = if let Some(ref first_key) = first_key {
                match current_schema.value_schema.as_ref() {
                    ValueSchema::Table(table_schema) => table_schema
                        .properties
                        .read()
                        .await
                        .contains_key(&SchemaAccessor::Key(first_key.to_string())),
                    _ => false,
                }
            } else {
                false
            };

            let schema_completions = value
                .find_completion_contents(
                    position,
                    keys,
                    accessors,
                    Some(&current_schema),
                    schema_context,
                    completion_hint,
                )
                .await;

            branch_results.push((branch_has_key, schema_completions));
        }

        let narrow_branches = branch_results.iter().any(|(has_key, _)| *has_key);
        for (branch_has_key, items) in branch_results {
            if !narrow_branches || branch_has_key {
                completion_items.extend(items);
            }
        }

        let detail = any_of_schema
            .detail(
                &current_schema.schema_uri,
                &current_schema.definitions,
                schema_context.store,
                completion_hint,
            )
            .await;

        let documentation = any_of_schema
            .documentation(
                &current_schema.schema_uri,
                &current_schema.definitions,
                schema_context.store,
                completion_hint,
            )
            .await;

        for completion_item in completion_items.iter_mut() {
            if completion_item.detail.is_none() {
                completion_item.detail = detail.clone();
            }
            if completion_item.documentation.is_none() {
                completion_item.documentation = documentation.clone();
            }
        }

        if !narrow_branches {
            if let Some(default) = &any_of_schema.default {
                let default_label = default.to_string();
                if let Some(completion_item) = completion_items
                    .iter_mut()
                    .find(|item| item.label == default_label)
                {
                    completion_item.priority = CompletionContentPriority::Default;
                } else if let Some(completion_item) = tombi_json_value_to_completion_default_item(
                    default,
                    position,
                    detail.clone(),
                    documentation.clone(),
                    Some(&current_schema.schema_uri),
                    completion_hint,
                ) {
                    completion_items.push(completion_item);
                }
            }

            if let Some(examples) = &any_of_schema.examples {
                for example in examples {
                    let example_label = example.to_string();
                    if completion_items
                        .iter()
                        .any(|item| item.label == example_label)
                    {
                        continue;
                    }

                    if let Some(completion_item) = tombi_json_value_to_completion_example_item(
                        example,
                        position,
                        detail.clone(),
                        documentation.clone(),
                        Some(&current_schema.schema_uri),
                        completion_hint,
                    ) {
                        completion_items.push(completion_item);
                    }
                }
            }
        }

        completion_items
    }
    .boxed()
}
