use std::rc::Rc;

use napi::bindgen_prelude::{External, FunctionCallContext};
use napi::threadsafe_function::ThreadsafeFunction;

use crate::vscode_sys;

pub struct Arguments {
    pub line_decorations: Vec<crate::inline_credit::LineDecoration>,
    pub text_editor_reference: Rc<vscode_sys::reference::TextEditorRef>,
    pub decoration_type_reference: Rc<vscode_sys::reference::TextEditorDecorationTypeRef>,
}

pub type Prototype =
    ThreadsafeFunction<External<Arguments>, (), External<Arguments>, napi::Status, false, false, 0>;

pub fn build(env: &napi::Env) -> Result<Prototype, napi::Error> {
    env.create_function_from_closure("set_text_editor_decorations", callback)?
        .build_threadsafe_function()
        .build()
}

fn callback(function_call_context: FunctionCallContext) -> Result<(), napi::Error> {
    let (external,): (&External<Arguments>,) = function_call_context.args()?;
    let Arguments {
        line_decorations,
        text_editor_reference,
        decoration_type_reference,
    } = &**external;

    let mut decoration_options_list = Vec::with_capacity(line_decorations.len());

    for crate::inline_credit::LineDecoration {
        line_number,
        line_annotation_text,
        hover_message_text,
    } in line_decorations
    {
        let annotation_line =
            vscode_sys::Position::new(function_call_context.env, *line_number as u32, u32::MAX)?;
        let annotation_range = vscode_sys::Range::new(
            function_call_context.env,
            &annotation_line,
            &annotation_line,
        )?;

        let mut hover_message =
            vscode_sys::MarkdownString::new(function_call_context.env, hover_message_text, false)?;
        hover_message.set_support_html(true)?;

        let after =
            vscode_sys::ThemableDecorationAttachmentRenderOptions::new(function_call_context.env)?
                .content_text(line_annotation_text)?;
        let render_options =
            vscode_sys::DecorationInstanceRenderOptions::new(function_call_context.env)?
                .after(after)?;
        let decoration_options =
            vscode_sys::DecorationOptions::new(function_call_context.env, &annotation_range)?
                .hover_message(&hover_message)?
                .render_options(render_options)?;

        decoration_options_list.push(decoration_options);
    }

    let text_editor = text_editor_reference.get_inner(function_call_context.env)?;
    let decoration_type = decoration_type_reference.get_inner(function_call_context.env)?;
    text_editor.set_decorations(decoration_type, decoration_options_list)?;

    Ok(())
}