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::{
    CreateNumberingRuleReq, GenerateNumberResp, NumberingRuleResp, UpdateNumberingRuleReq,
};
use crate::service::numbering_service::NumberingService;

#[utoipa::path(
    get,
    path = "/api/v1/numbering-rules",
    params(Pagination),
    responses(
        (status = 200, description = "成功", body = ApiResponse<PaginatedResponse<NumberingRuleResp>>),
        (status = 401, description = "未授权"),
        (status = 403, description = "权限不足"),
    ),
    security(("bearer_auth" = [])),
    tag = "编号规则"
)]
/// GET /api/v1/numbering-rules
///
/// 分页查询当前租户下的编号规则列表。
/// 需要 `numbering.list` 权限。
pub async fn list_numbering_rules<S>(
    State(state): State<ConfigState>,
    Extension(ctx): Extension<TenantContext>,
    Query(pagination): Query<Pagination>,
) -> Result<Json<ApiResponse<PaginatedResponse<NumberingRuleResp>>>, AppError>
where
    ConfigState: FromRef<S>,
    S: Clone + Send + Sync + 'static,
{
    require_permission(&ctx, "numbering.list")?;

    let (rules, total) = NumberingService::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: rules,
        total,
        page,
        page_size,
        total_pages,
    })))
}

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

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

    let rule = NumberingService::create(
        ctx.tenant_id,
        ctx.user_id,
        &req,
        &state.db,
        &state.event_bus,
    )
    .await?;

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

#[utoipa::path(
    put,
    path = "/api/v1/numbering-rules/{id}",
    params(("id" = Uuid, Path, description = "编号规则ID")),
    request_body = UpdateNumberingRuleReq,
    responses(
        (status = 200, description = "成功", body = ApiResponse<NumberingRuleResp>),
        (status = 401, description = "未授权"),
        (status = 403, description = "权限不足"),
    ),
    security(("bearer_auth" = [])),
    tag = "编号规则"
)]
/// PUT /api/v1/numbering-rules/:id
///
/// 更新编号规则的可编辑字段。
/// 需要 `numbering.update` 权限。
pub async fn update_numbering_rule<S>(
    State(state): State<ConfigState>,
    Extension(ctx): Extension<TenantContext>,
    Path(id): Path<Uuid>,
    Json(req): Json<UpdateNumberingRuleReq>,
) -> Result<Json<ApiResponse<NumberingRuleResp>>, AppError>
where
    ConfigState: FromRef<S>,
    S: Clone + Send + Sync + 'static,
{
    require_permission(&ctx, "numbering.update")?;

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

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

#[utoipa::path(
    post,
    path = "/api/v1/numbering-rules/{id}/generate",
    params(("id" = Uuid, Path, description = "编号规则ID")),
    responses(
        (status = 200, description = "成功", body = ApiResponse<GenerateNumberResp>),
        (status = 401, description = "未授权"),
        (status = 403, description = "权限不足"),
    ),
    security(("bearer_auth" = [])),
    tag = "编号规则"
)]
/// POST /api/v1/numbering-rules/:id/generate
///
/// 根据编号规则生成新的编号。
/// 使用 PostgreSQL advisory lock 保证并发安全。
/// 需要 `numbering.generate` 权限。
pub async fn generate_number<S>(
    State(state): State<ConfigState>,
    Extension(ctx): Extension<TenantContext>,
    Path(id): Path<Uuid>,
) -> Result<Json<ApiResponse<GenerateNumberResp>>, AppError>
where
    ConfigState: FromRef<S>,
    S: Clone + Send + Sync + 'static,
{
    require_permission(&ctx, "numbering.generate")?;

    let result = NumberingService::generate_number(id, ctx.tenant_id, &state.db).await?;

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

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

    NumberingService::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()),
    }))
}

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