use chromiumoxide::Page;
use chromiumoxide::cdp::browser_protocol::dom::{
    BackendNodeId as CdpBackendNodeId, GetDocumentParams, ResolveNodeParams,
};
use chromiumoxide::cdp::browser_protocol::input::{DispatchKeyEventParams, DispatchKeyEventType};
use chromiumoxide::cdp::js_protocol::runtime::CallFunctionOnParams;
use ras_errors::AppError;
use ras_types::BackendNodeId;

pub(crate) async fn click_backend_node(page: &Page, node: BackendNodeId) -> Result<(), AppError> {
    let _ = page.execute(GetDocumentParams::default()).await;
    let resolve = ResolveNodeParams::builder()
        .backend_node_id(CdpBackendNodeId::new(node.0))
        .build();
    let resolved = page
        .execute(resolve)
        .await
        .map_err(|e| AppError::ActionFailed(format!("resolve_node: {e}")))?;
    let object_id = resolved
        .result
        .object
        .object_id
        .clone()
        .ok_or_else(|| AppError::ActionFailed("resolve_node: no object_id".into()))?;
    let call = CallFunctionOnParams::builder()
        .object_id(object_id)
        .function_declaration(
            "function() { if (typeof this.focus === 'function') { this.focus(); } this.click(); }"
                .to_string(),
        )
        .build()
        .map_err(|e| AppError::ActionFailed(format!("call_fn params: {e}")))?;
    page.execute(call)
        .await
        .map_err(|e| AppError::ActionFailed(format!("click_node call: {e}")))?;
    Ok(())
}

pub(crate) async fn type_chars(page: &Page, text: &str) -> Result<(), AppError> {
    for ch in text.chars() {
        let params = DispatchKeyEventParams::builder()
            .r#type(DispatchKeyEventType::Char)
            .text(ch.to_string())
            .build()
            .map_err(|e| AppError::ActionFailed(format!("key params: {e}")))?;
        page.execute(params)
            .await
            .map_err(|e| AppError::ActionFailed(format!("type_text dispatch: {e}")))?;
    }
    Ok(())
}
