use reqwest::Url;
use serde_json::{json, Value};
use thiserror::Error;

use crate::pipe::Action;

const BAIDU_URL: &str = "https://www.baidu.com";
const BAIDU_DOMAIN: &str = "www.baidu.com";
const BAIDU_INPUT_SELECTOR: &str = "#kw";
const BAIDU_SEARCH_BUTTON_SELECTOR: &str = "#su";
const ZHIHU_URL: &str = "https://www.zhihu.com/search";
const ZHIHU_DOMAIN: &str = "www.zhihu.com";

#[derive(Debug, Clone, PartialEq)]
pub struct PlannedStep {
    pub action: Action,
    pub params: Value,
    pub expected_domain: String,
    pub log_message: String,
}

#[derive(Debug, Clone, PartialEq)]
pub struct TaskPlan {
    pub summary: String,
    pub steps: Vec<PlannedStep>,
}

#[derive(Debug, Error, Clone, PartialEq, Eq)]
pub enum PlannerError {
    #[error("unsupported instruction: {0}")]
    UnsupportedInstruction(String),
    #[error("missing search query in instruction")]
    MissingQuery,
}

pub fn plan_instruction(instruction: &str) -> Result<TaskPlan, PlannerError> {
    let trimmed = instruction.trim();
    if let Some(query) = extract_query(trimmed, &["打开百度搜索", "打开百度并搜索"])? {
        return Ok(plan_baidu_search(query));
    }

    if let Some(query) = extract_query(trimmed, &["打开知乎搜索", "打开知乎并搜索"])? {
        return Ok(plan_zhihu_search(query));
    }

    Err(PlannerError::UnsupportedInstruction(trimmed.to_string()))
}

fn extract_query<'a>(
    instruction: &'a str,
    prefixes: &[&str],
) -> Result<Option<&'a str>, PlannerError> {
    let Some(query) = prefixes
        .iter()
        .find_map(|prefix| instruction.strip_prefix(prefix))
    else {
        return Ok(None);
    };

    let query = query.trim();
    if query.is_empty() {
        return Err(PlannerError::MissingQuery);
    }

    Ok(Some(query))
}

fn plan_baidu_search(query: &str) -> TaskPlan {
    TaskPlan {
        summary: format!("已在百度搜索{query}"),
        steps: vec![
            PlannedStep {
                action: Action::Navigate,
                params: json!({ "url": BAIDU_URL }),
                expected_domain: BAIDU_DOMAIN.to_string(),
                log_message: "navigate https://www.baidu.com".to_string(),
            },
            PlannedStep {
                action: Action::Type,
                params: json!({
                    "selector": BAIDU_INPUT_SELECTOR,
                    "text": query,
                    "clear_first": true
                }),
                expected_domain: BAIDU_DOMAIN.to_string(),
                log_message: format!("type {query} into {BAIDU_INPUT_SELECTOR}"),
            },
            PlannedStep {
                action: Action::Click,
                params: json!({ "selector": BAIDU_SEARCH_BUTTON_SELECTOR }),
                expected_domain: BAIDU_DOMAIN.to_string(),
                log_message: format!("click {BAIDU_SEARCH_BUTTON_SELECTOR}"),
            },
        ],
    }
}

fn plan_zhihu_search(query: &str) -> TaskPlan {
    let url = Url::parse_with_params(ZHIHU_URL, &[("type", "content"), ("q", query)])
        .expect("valid Zhihu search URL");
    let url: String = url.into();

    TaskPlan {
        summary: format!("已在知乎搜索{query}"),
        steps: vec![PlannedStep {
            action: Action::Navigate,
            params: json!({ "url": url }),
            expected_domain: ZHIHU_DOMAIN.to_string(),
            log_message: format!("navigate {url}"),
        }],
    }
}
