use iri_string::types::UriAbsoluteString;

use crate::event_loop::ExtensionState;
use crate::event_loop::js_function::Functions;

#[tracing::instrument(skip(extension_state, js_functions))]
pub async fn handle(
    uri: UriAbsoluteString,
    extension_state: &ExtensionState,
    js_functions: &Functions,
) {
    let Some((repository_path, relative_path, repository)) = extension_state.get_repository(&uri)
    else {
        tracing::info!(message = "No matching repository for path", ?uri);
        return;
    };

    let Some(open_file) = repository.repository.get_open_file(relative_path) else {
        tracing::info!(
            message = "No open file found in repository",
            ?repository_path,
            ?relative_path
        );
        return;
    };

    let Some(text_editor_reference) = repository.open_editors.get(relative_path) else {
        tracing::error!(
            message = "No matching TextEditor found for path",
            ?repository_path,
            ?relative_path
        );
        return;
    };

    let editor_selections = match js_functions
        .get_text_editor_selections(text_editor_reference)
        .await
    {
        Ok(editor_selections) => editor_selections,
        Err(error) => {
            tracing::error!(message = "Failed to get text editor selections", ?error);
            return;
        }
    };

    let text_document_is_dirty = match js_functions
        .get_text_editor_is_dirty(text_editor_reference)
        .await
    {
        Ok(is_dirty) => is_dirty,
        Err(error) => {
            tracing::warn!(message = "Failed to get isDirty for text editor", ?error);
            false
        }
    };

    let mut line_decorations = Vec::new();

    for (start_line, end_line) in editor_selections {
        let selected_range = (start_line as usize)..=(end_line as usize);
        let credit_spans = open_file.credit(selected_range);

        for credit_span in credit_spans {
            for line in credit_span.lines {
                let line_annotation_text = match crate::inline_credit::line_annotation::render(
                    &credit_span.value,
                    open_file,
                    text_document_is_dirty,
                    &repository.repository,
                    &extension_state.localization_context,
                ) {
                    Ok(line_annotation_text) => line_annotation_text,
                    Err(error) => {
                        tracing::error!(message = "Failed to render line annotation", ?error);
                        return;
                    }
                };

                let hover_message_text = match crate::inline_credit::hover::render(
                    credit_span.value,
                    open_file,
                    &repository.repository,
                    &extension_state.localization_context,
                ) {
                    Ok(hover_message_text) => hover_message_text,
                    Err(error) => {
                        tracing::error!(message = "Failed to render hover message", ?error);
                        return;
                    }
                };

                line_decorations.push(crate::inline_credit::LineDecoration {
                    line_number: line,
                    line_annotation_text,
                    hover_message_text,
                })
            }
        }
    }

    if let Err(error) = js_functions
        .set_text_editor_decorations(
            line_decorations,
            text_editor_reference,
            &extension_state.decoration_type,
        )
        .await
    {
        tracing::error!(message = "Failed to set text editor decorations", ?error);
        return;
    }
}