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

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

use crate::config_state::ConfigState;
use crate::dto::{
    CreateDictionaryItemReq, CreateDictionaryReq, DictionaryItemResp, DictionaryResp,
    UpdateDictionaryItemReq, UpdateDictionaryReq,
};
use crate::service::dictionary_service::DictionaryService;

#[utoipa::path(
    get,
    path = "/api/v1/dictionaries",
    params(Pagination),
    responses(
        (status = 200, description = "成功", body = ApiResponse<PaginatedResponse<DictionaryResp>>),
        (status = 401, description = "未授权"),
        (status = 403, description = "权限不足"),
    ),
    security(("bearer_auth" = [])),
    tag = "字典管理"
)]
/// GET /api/v1/dictionaries
///
/// 分页查询当前租户下的字典列表。
/// 每个字典包含其关联的字典项。
/// 需要 `dictionary.list` 权限。
pub async fn list_dictionaries<S>(
    State(state): State<ConfigState>,
    Extension(ctx): Extension<TenantContext>,
    Query(pagination): Query<Pagination>,
) -> Result<Json<ApiResponse<PaginatedResponse<DictionaryResp>>>, AppError>
where
    ConfigState: FromRef<S>,
    S: Clone + Send + Sync + 'static,
{
    require_permission(&ctx, "dictionary.list")?;

    let (dictionaries, total) =
        DictionaryService::list(ctx.tenant_id, &pagination, &state.db).await?;

    let page = pagination.page.unwrap_or(1);
    let page_size = pagination.limit();
    let total_pages = total.div_ceil(page_size);

    Ok(Json(ApiResponse::ok(PaginatedResponse {
        data: dictionaries,
        total,
        page,
        page_size,
        total_pages,
    })))
}

#[utoipa::path(
    post,
    path = "/api/v1/dictionaries",
    request_body = CreateDictionaryReq,
    responses(
        (status = 200, description = "成功", body = ApiResponse<DictionaryResp>),
        (status = 401, description = "未授权"),
        (status = 403, description = "权限不足"),
    ),
    security(("bearer_auth" = [])),
    tag = "字典管理"
)]
/// POST /api/v1/dictionaries
///
/// 在当前租户下创建新字典。
/// 字典编码在租户内必须唯一。
/// 需要 `dictionary.create` 权限。
pub async fn create_dictionary<S>(
    State(state): State<ConfigState>,
    Extension(ctx): Extension<TenantContext>,
    Json(req): Json<CreateDictionaryReq>,
) -> Result<Json<ApiResponse<DictionaryResp>>, AppError>
where
    ConfigState: FromRef<S>,
    S: Clone + Send + Sync + 'static,
{
    require_permission(&ctx, "dictionary.create")?;

    req.validate()
        .map_err(|e| AppError::Validation(e.to_string()))?;

    let dictionary = DictionaryService::create(
        ctx.tenant_id,
        ctx.user_id,
        &req.name,
        &req.code,
        &req.description,
        &state.db,
        &state.event_bus,
    )
    .await?;

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

#[utoipa::path(
    put,
    path = "/api/v1/dictionaries/{id}",
    params(("id" = Uuid, Path, description = "字典ID")),
    request_body = UpdateDictionaryReq,
    responses(
        (status = 200, description = "成功", body = ApiResponse<DictionaryResp>),
        (status = 401, description = "未授权"),
        (status = 403, description = "权限不足"),
    ),
    security(("bearer_auth" = [])),
    tag = "字典管理"
)]
/// PUT /api/v1/dictionaries/:id
///
/// 更新字典的可编辑字段（名称、描述）。
/// 编码创建后不可更改。
/// 需要 `dictionary.update` 权限。
pub async fn update_dictionary<S>(
    State(state): State<ConfigState>,
    Extension(ctx): Extension<TenantContext>,
    Path(id): Path<Uuid>,
    Json(req): Json<UpdateDictionaryReq>,
) -> Result<Json<ApiResponse<DictionaryResp>>, AppError>
where
    ConfigState: FromRef<S>,
    S: Clone + Send + Sync + 'static,
{
    require_permission(&ctx, "dictionary.update")?;

    let dictionary =
        DictionaryService::update(id, ctx.tenant_id, ctx.user_id, &req, &state.db).await?;

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

#[utoipa::path(
    delete,
    path = "/api/v1/dictionaries/{id}",
    params(("id" = Uuid, Path, description = "字典ID")),
    request_body = DeleteVersionReq,
    responses(
        (status = 200, description = "成功"),
        (status = 401, description = "未授权"),
        (status = 403, description = "权限不足"),
    ),
    security(("bearer_auth" = [])),
    tag = "字典管理"
)]
/// DELETE /api/v1/dictionaries/:id
///
/// 软删除字典，设置 deleted_at 时间戳。
/// 需要请求体包含 version 字段用于乐观锁校验。
/// 需要 `dictionary.delete` 权限。
pub async fn delete_dictionary<S>(
    State(state): State<ConfigState>,
    Extension(ctx): Extension<TenantContext>,
    Path(id): Path<Uuid>,
    Json(req): Json<DeleteVersionReq>,
) -> Result<Json<ApiResponse<()>>, AppError>
where
    ConfigState: FromRef<S>,
    S: Clone + Send + Sync + 'static,
{
    require_permission(&ctx, "dictionary.delete")?;

    DictionaryService::delete(
        id,
        ctx.tenant_id,
        ctx.user_id,
        req.version,
        &state.db,
        &state.event_bus,
    )
    .await?;

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

#[utoipa::path(
    get,
    path = "/api/v1/dictionaries/items-by-code",
    params(("code" = String, Query, description = "字典编码")),
    responses(
        (status = 200, description = "成功", body = ApiResponse<Vec<DictionaryItemResp>>),
        (status = 401, description = "未授权"),
        (status = 403, description = "权限不足"),
    ),
    security(("bearer_auth" = [])),
    tag = "字典管理"
)]
/// GET /api/v1/dictionaries/items-by-code?code=xxx
///
/// 根据字典编码查询所有字典项。
/// 用于前端下拉框和枚举值查找。
/// 需要 `dictionary.list` 权限。
pub async fn list_items_by_code<S>(
    State(state): State<ConfigState>,
    Extension(ctx): Extension<TenantContext>,
    Query(query): Query<ItemsByCodeQuery>,
) -> Result<Json<ApiResponse<Vec<DictionaryItemResp>>>, AppError>
where
    ConfigState: FromRef<S>,
    S: Clone + Send + Sync + 'static,
{
    require_permission(&ctx, "dictionary.list")?;

    let items =
        DictionaryService::list_items_by_code(&query.code, ctx.tenant_id, &state.db).await?;

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

#[utoipa::path(
    post,
    path = "/api/v1/dictionaries/{dict_id}/items",
    params(("dict_id" = Uuid, Path, description = "字典ID")),
    request_body = CreateDictionaryItemReq,
    responses(
        (status = 200, description = "成功", body = ApiResponse<DictionaryItemResp>),
        (status = 401, description = "未授权"),
        (status = 403, description = "权限不足"),
    ),
    security(("bearer_auth" = [])),
    tag = "字典管理"
)]
/// POST /api/v1/dictionaries/:dict_id/items
///
/// 向指定字典添加新的字典项。
/// 字典项的 value 在同一字典内必须唯一。
/// 需要 `dictionary.create` 权限。
pub async fn create_item<S>(
    State(state): State<ConfigState>,
    Extension(ctx): Extension<TenantContext>,
    Path(dict_id): Path<Uuid>,
    Json(req): Json<CreateDictionaryItemReq>,
) -> Result<Json<ApiResponse<DictionaryItemResp>>, AppError>
where
    ConfigState: FromRef<S>,
    S: Clone + Send + Sync + 'static,
{
    require_permission(&ctx, "dictionary.create")?;

    req.validate()
        .map_err(|e| AppError::Validation(e.to_string()))?;

    let item =
        DictionaryService::add_item(dict_id, ctx.tenant_id, ctx.user_id, &req, &state.db).await?;

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

#[utoipa::path(
    put,
    path = "/api/v1/dictionaries/{dict_id}/items/{item_id}",
    params(
        ("dict_id" = Uuid, Path, description = "字典ID"),
        ("item_id" = Uuid, Path, description = "字典项ID"),
    ),
    request_body = UpdateDictionaryItemReq,
    responses(
        (status = 200, description = "成功", body = ApiResponse<DictionaryItemResp>),
        (status = 401, description = "未授权"),
        (status = 403, description = "权限不足"),
    ),
    security(("bearer_auth" = [])),
    tag = "字典管理"
)]
/// PUT /api/v1/dictionaries/:dict_id/items/:item_id
///
/// 更新字典项的可编辑字段（label、value、sort_order、color）。
/// 需要 `dictionary.update` 权限。
pub async fn update_item<S>(
    State(state): State<ConfigState>,
    Extension(ctx): Extension<TenantContext>,
    Path((dict_id, item_id)): Path<(Uuid, Uuid)>,
    Json(req): Json<UpdateDictionaryItemReq>,
) -> Result<Json<ApiResponse<DictionaryItemResp>>, AppError>
where
    ConfigState: FromRef<S>,
    S: Clone + Send + Sync + 'static,
{
    require_permission(&ctx, "dictionary.update")?;

    // 验证 item_id 属于 dict_id
    let item = DictionaryService::update_item(item_id, ctx.tenant_id, ctx.user_id, &req, &state.db)
        .await?;

    // 确保 item 属于指定的 dictionary
    if item.dictionary_id != dict_id {
        return Err(AppError::Validation("字典项不属于指定的字典".to_string()));
    }

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

#[utoipa::path(
    delete,
    path = "/api/v1/dictionaries/{dict_id}/items/{item_id}",
    params(
        ("dict_id" = Uuid, Path, description = "字典ID"),
        ("item_id" = Uuid, Path, description = "字典项ID"),
    ),
    request_body = DeleteVersionReq,
    responses(
        (status = 200, description = "成功"),
        (status = 401, description = "未授权"),
        (status = 403, description = "权限不足"),
    ),
    security(("bearer_auth" = [])),
    tag = "字典管理"
)]
/// DELETE /api/v1/dictionaries/:dict_id/items/:item_id
///
/// 软删除字典项，设置 deleted_at 时间戳。
/// 需要请求体包含 version 字段用于乐观锁校验。
/// 需要 `dictionary.delete` 权限。
pub async fn delete_item<S>(
    State(state): State<ConfigState>,
    Extension(ctx): Extension<TenantContext>,
    Path((_dict_id, item_id)): Path<(Uuid, Uuid)>,
    Json(req): Json<DeleteVersionReq>,
) -> Result<Json<ApiResponse<()>>, AppError>
where
    ConfigState: FromRef<S>,
    S: Clone + Send + Sync + 'static,
{
    require_permission(&ctx, "dictionary.delete")?;

    DictionaryService::delete_item(item_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)]
pub struct ItemsByCodeQuery {
    pub code: String,
}

/// 删除操作的乐观锁版本号。
#[derive(Debug, serde::Deserialize, utoipa::ToSchema)]
pub struct DeleteVersionReq {
    pub version: i32,
}
