use axum::Extension;
use axum::extract::{FromRef, Path, Query, State};
use axum::response::Json;

use erp_core::error::AppError;
use erp_core::rbac::require_permission;
use erp_core::types::{ApiResponse, TenantContext};
use uuid::Uuid;

use crate::config_state::ConfigState;
use crate::dto::{SetSettingParams, SettingResp, UpdateSettingReq};
use crate::service::setting_service::SettingService;

#[utoipa::path(
    get,
    path = "/api/v1/settings/{key}",
    params(
        ("key" = String, Path, description = "设置键名"),
        ("scope" = Option<String>, Query, description = "作用域"),
        ("scope_id" = Option<Uuid>, Query, description = "作用域ID"),
    ),
    responses(
        (status = 200, description = "成功", body = ApiResponse<SettingResp>),
        (status = 401, description = "未授权"),
        (status = 403, description = "权限不足"),
    ),
    security(("bearer_auth" = [])),
    tag = "系统设置"
)]
/// GET /api/v1/settings/:key?scope=tenant&scope_id=xxx
///
/// 获取设置值，支持分层回退查找。
/// 解析顺序：精确匹配 -> 按作用域层级向上回退。
/// 需要 `setting.read` 权限。
pub async fn get_setting<S>(
    State(state): State<ConfigState>,
    Extension(ctx): Extension<TenantContext>,
    Path(key): Path<String>,
    Query(query): Query<SettingQuery>,
) -> Result<Json<ApiResponse<SettingResp>>, AppError>
where
    ConfigState: FromRef<S>,
    S: Clone + Send + Sync + 'static,
{
    require_permission(&ctx, "setting.read")?;

    let scope = query.scope.unwrap_or_else(|| "tenant".to_string());

    let setting =
        SettingService::get(&key, &scope, &query.scope_id, ctx.tenant_id, &state.db).await?;

    Ok(Json(ApiResponse::ok(setting)))
}

#[utoipa::path(
    put,
    path = "/api/v1/settings/{key}",
    params(("key" = String, Path, description = "设置键名")),
    request_body = UpdateSettingReq,
    responses(
        (status = 200, description = "成功", body = ApiResponse<SettingResp>),
        (status = 401, description = "未授权"),
        (status = 403, description = "权限不足"),
    ),
    security(("bearer_auth" = [])),
    tag = "系统设置"
)]
/// PUT /api/v1/settings/:key
///
/// 创建或更新设置值。
/// 如果相同 (scope, scope_id, key) 的记录存在则更新，否则插入。
/// 需要 `setting.update` 权限。
pub async fn update_setting<S>(
    State(state): State<ConfigState>,
    Extension(ctx): Extension<TenantContext>,
    Path(key): Path<String>,
    Json(req): Json<UpdateSettingReq>,
) -> Result<Json<ApiResponse<SettingResp>>, AppError>
where
    ConfigState: FromRef<S>,
    S: Clone + Send + Sync + 'static,
{
    require_permission(&ctx, "setting.update")?;

    let setting = SettingService::set(
        SetSettingParams {
            key,
            scope: "tenant".to_string(),
            scope_id: None,
            value: req.setting_value,
            version: req.version,
        },
        ctx.tenant_id,
        ctx.user_id,
        &state.db,
        &state.event_bus,
    )
    .await?;

    Ok(Json(ApiResponse::ok(setting)))
}

/// 设置查询参数。
#[derive(Debug, serde::Deserialize)]
pub struct SettingQuery {
    pub scope: Option<String>,
    pub scope_id: Option<Uuid>,
}

#[utoipa::path(
    delete,
    path = "/api/v1/settings/{key}",
    params(
        ("key" = String, Path, description = "设置键名"),
        ("scope" = Option<String>, Query, description = "作用域"),
        ("scope_id" = Option<Uuid>, Query, description = "作用域ID"),
    ),
    request_body = DeleteSettingVersionReq,
    responses(
        (status = 200, description = "成功"),
        (status = 401, description = "未授权"),
        (status = 403, description = "权限不足"),
    ),
    security(("bearer_auth" = [])),
    tag = "系统设置"
)]
/// DELETE /api/v1/settings/:key
///
/// 软删除设置值，设置 deleted_at 时间戳。
/// 需要请求体包含 version 字段用于乐观锁校验。
/// 需要 `setting.delete` 权限。
pub async fn delete_setting<S>(
    State(state): State<ConfigState>,
    Extension(ctx): Extension<TenantContext>,
    Path(key): Path<String>,
    Query(query): Query<SettingQuery>,
    Json(req): Json<DeleteSettingVersionReq>,
) -> Result<Json<ApiResponse<()>>, AppError>
where
    ConfigState: FromRef<S>,
    S: Clone + Send + Sync + 'static,
{
    require_permission(&ctx, "setting.delete")?;

    let scope = query.scope.unwrap_or_else(|| "tenant".to_string());

    SettingService::delete(
        &key,
        &scope,
        &query.scope_id,
        ctx.tenant_id,
        ctx.user_id,
        req.version,
        &state.db,
    )
    .await?;

    Ok(Json(ApiResponse {
        success: true,
        data: None,
        message: Some("设置已删除".to_string()),
    }))
}

/// 删除设置的乐观锁版本号请求体。
#[derive(Debug, serde::Deserialize, utoipa::ToSchema)]
pub struct DeleteSettingVersionReq {
    pub version: i32,
}
