use axum::Router;
use axum::extract::{Extension, FromRef, Query, State};
use axum::response::Json;
use axum::routing::get;
use sea_orm::{ColumnTrait, EntityTrait, PaginatorTrait, QueryFilter, QueryOrder};
use serde::{Deserialize, Serialize};

use erp_core::entity::audit_log;
use erp_core::error::AppError;
use erp_core::types::{ApiResponse, PaginatedResponse, TenantContext};

#[derive(Debug, Deserialize)]
pub struct AuditLogQuery {
    pub resource_type: Option<String>,
    pub user_id: Option<uuid::Uuid>,
    pub page: Option<u64>,
    pub page_size: Option<u64>,
}

#[derive(Debug, Serialize)]
pub struct AuditLogResp {
    pub id: uuid::Uuid,
    pub tenant_id: uuid::Uuid,
    pub user_id: Option<uuid::Uuid>,
    pub user_name: Option<String>,
    pub action: String,
    pub resource_type: String,
    pub resource_id: Option<uuid::Uuid>,
    pub old_value: Option<serde_json::Value>,
    pub new_value: Option<serde_json::Value>,
    pub ip_address: Option<String>,
    pub user_agent: Option<String>,
    pub created_at: chrono::DateTime<chrono::Utc>,
}

impl From<audit_log::Model> for AuditLogResp {
    fn from(m: audit_log::Model) -> Self {
        Self {
            id: m.id,
            tenant_id: m.tenant_id,
            user_id: m.user_id,
            user_name: None,
            action: m.action,
            resource_type: m.resource_type,
            resource_id: m.resource_id,
            old_value: m.old_value,
            new_value: m.new_value,
            ip_address: m.ip_address,
            user_agent: m.user_agent,
            created_at: m.created_at,
        }
    }
}

async fn resolve_user_names(
    db: &sea_orm::DatabaseConnection,
    items: &[audit_log::Model],
) -> std::collections::HashMap<uuid::Uuid, String> {
    use erp_auth::entity::user;

    let user_ids: Vec<uuid::Uuid> = items
        .iter()
        .filter_map(|i| i.user_id)
        .collect::<std::collections::HashSet<_>>()
        .into_iter()
        .collect();

    if user_ids.is_empty() {
        return std::collections::HashMap::new();
    }

    let users = user::Entity::find()
        .filter(user::Column::Id.is_in(user_ids))
        .all(db)
        .await
        .unwrap_or_default();

    users
        .into_iter()
        .map(|u| {
            let name = u
                .display_name
                .filter(|n| !n.is_empty())
                .unwrap_or(u.username);
            (u.id, name)
        })
        .collect()
}

/// GET /audit-logs
pub async fn list_audit_logs<S>(
    State(db): State<sea_orm::DatabaseConnection>,
    Extension(ctx): Extension<TenantContext>,
    Query(params): Query<AuditLogQuery>,
) -> Result<Json<ApiResponse<PaginatedResponse<AuditLogResp>>>, AppError>
where
    sea_orm::DatabaseConnection: FromRef<S>,
    S: Clone + Send + Sync + 'static,
{
    let page = params.page.unwrap_or(1).max(1);
    let page_size = params.page_size.unwrap_or(20).min(100);
    let tenant_id = ctx.tenant_id;

    let mut q = audit_log::Entity::find().filter(audit_log::Column::TenantId.eq(tenant_id));

    if let Some(rt) = &params.resource_type {
        q = q.filter(audit_log::Column::ResourceType.eq(rt.clone()));
    }
    if let Some(uid) = &params.user_id {
        q = q.filter(audit_log::Column::UserId.eq(*uid));
    }

    let paginator = q
        .order_by_desc(audit_log::Column::CreatedAt)
        .paginate(&db, page_size);

    let total = paginator
        .num_items()
        .await
        .map_err(|e| AppError::Internal(format!("查询审计日志失败: {e}")))?;

    let items = paginator
        .fetch_page(page - 1)
        .await
        .map_err(|e| AppError::Internal(format!("查询审计日志失败: {e}")))?;

    let user_map = resolve_user_names(&db, &items).await;

    let resp_items: Vec<AuditLogResp> = items
        .into_iter()
        .map(|m| {
            let user_name = m.user_id.and_then(|uid| user_map.get(&uid).cloned());
            let mut resp = AuditLogResp::from(m);
            resp.user_name = user_name;
            resp
        })
        .collect();

    let total_pages = total.div_ceil(page_size);

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

pub fn audit_log_router<S>() -> Router<S>
where
    sea_orm::DatabaseConnection: FromRef<S>,
    S: Clone + Send + Sync + 'static,
{
    Router::new().route("/audit-logs", get(list_audit_logs))
}
