use crate::app::AppState;
use crate::context::RequestContext;
use crate::error::WebError;
use crate::hermes_tools::{ensure_write_authorized, ToolCallInput};
use crate::routes;
use axum::http::StatusCode;
use serde::Deserialize;
use serde_json::{json, Value};

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct IndexSettingsArgs {
    #[serde(default)]
    include_paths: Vec<String>,
    #[serde(default)]
    schedule_mode: Option<String>,
    #[serde(default)]
    schedule_time: Option<String>,
    #[serde(default)]
    schedule_date: Option<String>,
    #[serde(default)]
    run_on_change: Option<bool>,
}

pub async fn index_status(
    state: &AppState,
    context: &RequestContext,
    input: &ToolCallInput,
) -> Result<Value, WebError> {
    let workspace_id = effective_workspace_id(context, input);
    let root_uri = required_root_uri(
        context,
        input,
        "mnote_index_root_required",
        "本地索引状态缺少 rootUri",
    )?;
    let root_path =
        routes::ensure_local_workspace_read_access_with_state(state, context, &root_uri)
            .map_err(|error| error.with_context(context))?;
    let user_settings = read_user_settings(state, context, &workspace_id, &root_path)?;
    let effective_settings = routes::effective_local_index_settings_for_root(
        state.control_plane(),
        &workspace_id,
        &root_path,
    )?;
    let status = routes::local_index_status_with_settings(
        &root_path,
        &root_uri,
        &workspace_id,
        &user_settings,
        &effective_settings,
    )?;
    Ok(json!({
        "ok": true,
        "schema": "mnote.index.status_result.v1",
        "result": status
    }))
}

pub async fn index_refresh(
    state: &AppState,
    context: &RequestContext,
    input: &ToolCallInput,
) -> Result<Value, WebError> {
    let workspace_id = effective_workspace_id(context, input);
    let root_uri = required_root_uri(
        context,
        input,
        "mnote_index_root_required",
        "本地索引刷新缺少 rootUri",
    )?;
    let root_path =
        routes::ensure_local_workspace_read_access_with_state(state, context, &root_uri)
            .map_err(|error| error.with_context(context))?;
    let effective_settings = routes::effective_local_index_settings_for_root(
        state.control_plane(),
        &workspace_id,
        &root_path,
    )?;
    let refreshed = routes::refresh_local_search_index_with_settings(
        &root_path,
        &root_uri,
        &workspace_id,
        &effective_settings,
    )?;
    Ok(json!({
        "ok": true,
        "schema": "mnote.index.refresh_result.v1",
        "index": refreshed
    }))
}

pub async fn index_update_settings(
    state: &AppState,
    context: &RequestContext,
    input: &ToolCallInput,
) -> Result<Value, WebError> {
    ensure_write_authorized(context, input)?;
    let workspace_id = effective_workspace_id(context, input);
    let root_uri = required_root_uri(
        context,
        input,
        "mnote_index_root_required",
        "本地索引设置缺少 rootUri",
    )?;
    let args = parse_settings_args(context, input)?;
    let root_path =
        routes::ensure_local_workspace_write_access_with_state(state, context, &root_uri)
            .map_err(|error| error.with_context(context))?;
    let actor_id = routes::current_actor_id(state, context).ok_or_else(|| {
        WebError::new(
            StatusCode::UNAUTHORIZED,
            "local_index_settings_auth_required",
            "本地索引设置需要登录用户",
        )
        .with_context(context)
    })?;
    if input.dry_run == Some(true) {
        let planned_settings = routes::preview_user_local_index_settings(
            state.control_plane(),
            &actor_id,
            &workspace_id,
            &root_path,
            &args.include_paths,
            args.schedule_mode.as_deref(),
            args.schedule_time.as_deref(),
            args.schedule_date.as_deref(),
            args.run_on_change,
        )?;
        let current_status = index_status(state, context, input).await?;
        let would_clear_index_files = planned_settings.include_paths.is_empty();
        return Ok(json!({
            "ok": true,
            "schema": "mnote.index.update_settings_plan.v1",
            "dryRun": true,
            "plannedSettings": planned_settings,
            "currentStatus": current_status.get("result").cloned().unwrap_or(Value::Null),
            "wouldRefresh": true,
            "wouldClearIndexFiles": would_clear_index_files
        }));
    }
    let settings = routes::write_user_local_index_settings(
        state.control_plane(),
        &actor_id,
        &workspace_id,
        &root_path,
        &args.include_paths,
        args.schedule_mode.as_deref(),
        args.schedule_time.as_deref(),
        args.schedule_date.as_deref(),
        args.run_on_change,
    )?;
    let effective_settings = routes::effective_local_index_settings_for_root(
        state.control_plane(),
        &workspace_id,
        &root_path,
    )?;
    let refreshed = routes::refresh_local_search_index_with_settings(
        &root_path,
        &root_uri,
        &workspace_id,
        &effective_settings,
    )?;
    let status = routes::local_index_status_with_settings(
        &root_path,
        &root_uri,
        &workspace_id,
        &settings,
        &effective_settings,
    )?;
    Ok(json!({
        "ok": true,
        "schema": "mnote.index.update_settings_result.v1",
        "settings": settings,
        "index": refreshed,
        "result": status
    }))
}

fn parse_settings_args(
    context: &RequestContext,
    input: &ToolCallInput,
) -> Result<IndexSettingsArgs, WebError> {
    let args = input.args.clone().unwrap_or_else(|| json!({}));
    serde_json::from_value::<IndexSettingsArgs>(args).map_err(|error| {
        WebError::bad_request_code(
            "mnote_index_settings_payload_invalid",
            format!("本地索引设置参数无效: {error}"),
        )
        .with_context(context)
    })
}

fn read_user_settings(
    state: &AppState,
    context: &RequestContext,
    workspace_id: &str,
    root_path: &std::path::Path,
) -> Result<routes::LocalIndexSettings, WebError> {
    if let Some(actor_id) = routes::current_actor_id(state, context) {
        return routes::read_user_local_index_settings(
            state.control_plane(),
            &actor_id,
            workspace_id,
            root_path,
        );
    }
    routes::read_local_index_settings_or_default(root_path)
}

fn effective_workspace_id(context: &RequestContext, input: &ToolCallInput) -> String {
    input
        .effective_workspace_id()
        .or_else(|| context.workspace.workspace_id.clone())
        .unwrap_or_else(|| "default".into())
}

fn required_root_uri(
    context: &RequestContext,
    input: &ToolCallInput,
    code: &'static str,
    message: &'static str,
) -> Result<String, WebError> {
    input
        .effective_root_uri()
        .map(|value| value.trim().to_string())
        .filter(|value| !value.is_empty())
        .ok_or_else(|| WebError::bad_request_code(code, message).with_context(context))
}
