use crate::context::RequestContext;
use crate::error::WebError;
use axum::extract::Query;
use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use axum::{Extension, Json};
use serde::{Deserialize, Serialize};
use serde_json::json;

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MediaSignQuery {
    asset_id: Option<String>,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FileTreeUploadTargetPreflightPayload {
    workspace_id: Option<String>,
    target_document_id: Option<String>,
    target_row_id: Option<String>,
    focused_row_id: Option<String>,
    active_document_id: Option<String>,
    rows: Option<Vec<FileTreeUploadTargetRow>>,
    document_workspaces: Option<Vec<FileTreeDocumentWorkspace>>,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
#[allow(dead_code)]
pub struct FileTreeUploadTargetRow {
    row_id: Option<String>,
    row_kind: Option<String>,
    document_id: Option<String>,
    asset_id: Option<String>,
    asset_document_id: Option<String>,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FileTreeDocumentWorkspace {
    document_id: Option<String>,
    workspace_id: Option<String>,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FileTreeUploadTargetPlan {
    workspace_id: String,
    target_document_id: String,
    target_mindmap_id: Option<String>,
    target_sub_path: Option<String>,
}

fn trim_string(value: Option<&String>) -> Option<String> {
    value
        .map(String::as_str)
        .map(str::trim)
        .filter(|value| !value.is_empty())
        .map(ToOwned::to_owned)
}

fn workspace_for_document(
    document_id: &str,
    fallback_workspace_id: Option<&str>,
    document_workspaces: &[FileTreeDocumentWorkspace],
) -> Option<String> {
    for item in document_workspaces {
        if trim_string(item.document_id.as_ref()).as_deref() == Some(document_id) {
            if let Some(workspace_id) = trim_string(item.workspace_id.as_ref()) {
                return Some(workspace_id);
            }
        }
    }
    fallback_workspace_id
        .map(str::trim)
        .filter(|value| !value.is_empty())
        .map(ToOwned::to_owned)
}

fn document_for_target_row(
    target_row_id: Option<&str>,
    rows: &[FileTreeUploadTargetRow],
) -> Option<String> {
    let row_id = target_row_id?.trim();
    if row_id.is_empty() {
        return None;
    }
    for row in rows {
        if trim_string(row.row_id.as_ref()).as_deref() != Some(row_id) {
            continue;
        }
        if let Some(document_id) = trim_string(row.document_id.as_ref()) {
            return Some(document_id);
        }
        if let Some(document_id) = trim_string(row.asset_document_id.as_ref()) {
            return Some(document_id);
        }
    }
    None
}

pub async fn upload(Extension(context): Extension<RequestContext>) -> Response {
    retired_convex_media_response(&context, "upload", None)
}

pub async fn filetree_upload_target_preflight(
    Extension(context): Extension<RequestContext>,
    Json(payload): Json<FileTreeUploadTargetPreflightPayload>,
) -> Result<Response, WebError> {
    let rows = payload.rows.unwrap_or_default();
    let document_workspaces = payload.document_workspaces.unwrap_or_default();
    let fallback_workspace_id = trim_string(payload.workspace_id.as_ref());
    let target_row_document_id = document_for_target_row(
        trim_string(payload.target_row_id.as_ref()).as_deref(),
        &rows,
    );
    let document_id = trim_string(payload.target_document_id.as_ref())
        .or(target_row_document_id)
        .or_else(|| {
            trim_string(payload.focused_row_id.as_ref())
                .and_then(|row_id| document_for_target_row(Some(&row_id), &rows))
        })
        .or_else(|| trim_string(payload.active_document_id.as_ref()))
        .ok_or_else(|| {
            WebError::bad_request_code(
                "filetree_upload_target_document_missing",
                "请选择一个目标页面后再上传文件",
            )
        })?;
    let workspace_id = workspace_for_document(
        &document_id,
        fallback_workspace_id
            .as_deref()
            .or(context.workspace.workspace_id.as_deref()),
        &document_workspaces,
    )
    .ok_or_else(|| {
        WebError::bad_request_code(
            "filetree_upload_target_workspace_missing",
            "缺少 workspaceId",
        )
    })?;
    let plan = FileTreeUploadTargetPlan {
        workspace_id,
        target_document_id: document_id,
        target_mindmap_id: None,
        target_sub_path: None,
    };
    Ok(Json(json!({
        "requestId": context.trace.request_id,
        "traceId": context.trace.trace_id,
        "plan": plan,
    }))
    .into_response())
}

pub async fn sign(
    Extension(context): Extension<RequestContext>,
    Query(query): Query<MediaSignQuery>,
) -> Response {
    let asset_id = query
        .asset_id
        .as_deref()
        .map(str::trim)
        .filter(|value| !value.is_empty());
    retired_convex_media_response(&context, "sign", asset_id)
}

fn retired_convex_media_response(
    context: &RequestContext,
    operation: &str,
    asset_id: Option<&str>,
) -> Response {
    (
        StatusCode::GONE,
        Json(json!({
            "ok": false,
            "code": "mnote_media_convex_retired",
            "error": "旧 Convex Files media route 已退役",
            "message": "旧 /api/media Convex Files 上传与签名链已退役；local-first 附件请使用 /api/local-folder/assets/upload 与 /api/local-folder/files/open。",
            "operation": operation,
            "assetId": asset_id,
            "replacement": {
                "upload": "/api/local-folder/assets/upload",
                "open": "/api/local-folder/files/open",
                "preflight": "/api/tree/filetree/upload-target-preflight"
            },
            "requestId": context.trace.request_id,
            "traceId": context.trace.trace_id,
        })),
    )
        .into_response()
}
