use leptos_tiptap::TiptapRange;
use wasm_bindgen::{JsCast, JsValue};
use web_sys::{window, Element};

const EDITOR_ROOT_SELECTOR: &str = ".editor-surface .ProseMirror";

fn editor_root_element() -> Option<Element> {
    window()
        .and_then(|win| win.document())
        .and_then(|document| document.query_selector(EDITOR_ROOT_SELECTOR).ok().flatten())
}

fn selected_local_attachment_link_dom_info() -> Option<(String, String)> {
    let selection = match window().and_then(|win| win.get_selection().ok().flatten()) {
        Some(selection) if !selection.is_collapsed() => selection,
        _ => return None,
    };
    let selected_text = String::from(selection.to_string()).trim().to_string();
    if selected_text.is_empty() {
        return None;
    }

    let root = editor_root_element()?;
    let links = root.get_elements_by_tag_name("a");
    for index in 0..links.length() {
        let Some(link) = links.item(index) else {
            continue;
        };
        let href = link.get_attribute("href").unwrap_or_default();
        if !href.contains("/api/local-folder/files/open") {
            continue;
        }
        if link.text_content().unwrap_or_default().trim() == selected_text {
            return Some((selected_text, href));
        }
    }

    None
}

/// 当前 DOM 选中文本匹配本地附件链接文本时返回 `true`。
pub(crate) fn selected_local_attachment_link_text() -> bool {
    selected_local_attachment_link_dom_info().is_some()
}

/// 当前非空选区命中本地附件链接 mark 时返回 `true`。
pub(crate) fn selected_local_attachment_link_in_editor_state() -> bool {
    let Some(root) = editor_root_element() else {
        return false;
    };
    let Ok(editor_value) = js_sys::Reflect::get(root.as_ref(), &JsValue::from_str("editor")) else {
        return false;
    };
    let Ok(state_value) = js_sys::Reflect::get(&editor_value, &JsValue::from_str("state")) else {
        return false;
    };
    let Ok(selection_value) = js_sys::Reflect::get(&state_value, &JsValue::from_str("selection"))
    else {
        return false;
    };
    let is_empty = js_sys::Reflect::get(&selection_value, &JsValue::from_str("empty"))
        .ok()
        .and_then(|value| value.as_bool())
        .unwrap_or(true);
    if is_empty {
        return false;
    }
    let Some(get_attributes) =
        js_sys::Reflect::get(&editor_value, &JsValue::from_str("getAttributes"))
            .ok()
            .and_then(|value| value.dyn_into::<js_sys::Function>().ok())
    else {
        return false;
    };
    let Ok(attributes) = get_attributes.call1(&editor_value, &JsValue::from_str("link")) else {
        return false;
    };
    js_sys::Reflect::get(&attributes, &JsValue::from_str("href"))
        .ok()
        .and_then(|value| value.as_string())
        .is_some_and(|href| href.contains("/api/local-folder/files/open"))
}

/// DOM selection 或 Tiptap editor state 任一路径命中本地附件链接即可。
pub(crate) fn is_local_attachment_link_selected() -> bool {
    selected_local_attachment_link_in_editor_state() || selected_local_attachment_link_text()
}

pub(crate) fn selected_local_attachment_link_range() -> Option<TiptapRange> {
    let (selected_text, selected_href) = selected_local_attachment_link_dom_info()?;
    let root = editor_root_element()?;
    let editor_value = js_sys::Reflect::get(root.as_ref(), &JsValue::from_str("editor")).ok()?;
    let find_range = js_sys::Function::new_with_args(
        "editor, selectedText, selectedHref",
        r#"
        if (!editor || !editor.state || !editor.state.doc) return null;
        var text = String(selectedText || '').trim();
        var hrefNeedle = String(selectedHref || '').trim();
        if (!text) return null;
        var match = null;
        editor.state.doc.descendants(function(node, pos) {
          if (match || !node || !node.isText) return;
          var nodeText = String(node.text || '');
          var offset = nodeText.indexOf(text);
          if (offset < 0) return;
          var hasAttachmentLink = Array.isArray(node.marks) && node.marks.some(function(mark) {
            var href = mark && mark.attrs ? String(mark.attrs.href || '') : '';
            return mark.type && mark.type.name === 'link'
              && href.indexOf('/api/local-folder/files/open') >= 0
              && (!hrefNeedle || href === hrefNeedle);
          });
          if (!hasAttachmentLink) return;
          match = { from: pos + offset, to: pos + offset + text.length };
        });
        return match;
        "#,
    );
    let range_value = find_range
        .call3(
            &JsValue::NULL,
            &editor_value,
            &JsValue::from_str(&selected_text),
            &JsValue::from_str(&selected_href),
        )
        .ok()?;
    if range_value.is_null() || range_value.is_undefined() {
        return None;
    }
    let from = js_sys::Reflect::get(&range_value, &JsValue::from_str("from"))
        .ok()
        .and_then(|value| value.as_f64())
        .filter(|value| value.is_finite() && *value >= 0.0)? as u32;
    let to = js_sys::Reflect::get(&range_value, &JsValue::from_str("to"))
        .ok()
        .and_then(|value| value.as_f64())
        .filter(|value| value.is_finite() && *value >= from as f64)? as u32;
    Some(TiptapRange { from, to })
}
