use crate::AppState;
use axum::{
    extract::{Query, State},
    http::StatusCode,
    response::IntoResponse,
    routing::get,
    Json, Router,
};
use chrono::{DateTime, Utc};
use contracts::auth_middleware::AuthUser;
use serde::{Deserialize, Serialize};
use uuid::Uuid;

pub fn router() -> Router<AppState> {
    Router::new().route("/", get(list_activity_logs))
}

#[derive(Deserialize)]
struct ListQuery {
    actor_id: Option<Uuid>,
    entity_type: Option<String>,
    action: Option<String>,
    from: Option<DateTime<Utc>>,
    to: Option<DateTime<Utc>>,
    page: Option<i64>,
    limit: Option<i64>,
}

#[derive(Serialize, Deserialize, sqlx::FromRow)]
struct ActivityLogDto {
    id: Uuid,
    actor_id: Uuid,
    actor_type: String,
    entity_id: Uuid,
    entity_type: String,
    action: String,
    metadata: Option<serde_json::Value>,
    ip_address: Option<String>,
    user_agent: Option<String>,
    created_at: String,
}

#[derive(Serialize)]
struct PaginatedResponse {
    data: Vec<ActivityLogDto>,
    total: i64,
    page: i64,
    total_pages: i64,
}

async fn list_activity_logs(
    _auth: AuthUser,
    State(state): State<AppState>,
    Query(params): Query<ListQuery>,
) -> impl IntoResponse {
    // Ensure admin permission (require_admin will be applied by router if nested under /api/admin)
    let page = params.page.unwrap_or(1).max(1);
    let limit = params.limit.unwrap_or(50).clamp(1, 100);
    let offset = (page - 1) * limit;

    let actor_id = params.actor_id;
    let entity_type = params.entity_type.as_deref().unwrap_or_default();
    let action = params.action.as_deref().unwrap_or_default();
    let from_dt = params.from;
    let to_dt = params.to;

    let total: i64 = sqlx::query_scalar::<_, i64>(
        r#"
        SELECT COUNT(*) FROM activity_logs
        WHERE ($1::uuid IS NULL OR actor_id = $1)
          AND ($2::text IS NULL OR entity_type = $2)
          AND ($3::text IS NULL OR action = $3)
          AND ($4::timestamptz IS NULL OR created_at >= $4)
          AND ($5::timestamptz IS NULL OR created_at <= $5)
        "#,
    )
    .bind(actor_id)
    .bind(if entity_type.is_empty() { None } else { Some(entity_type) })
    .bind(if action.is_empty() { None } else { Some(action) })
    .bind(from_dt)
    .bind(to_dt)
    .fetch_one(&state.pool)
    .await
    .unwrap_or(0);

    let rows = sqlx::query_as::<_, ActivityLogDto>(
        r#"
        SELECT id, actor_id, actor_type, entity_id, entity_type, action, metadata, ip_address, user_agent, created_at
        FROM activity_logs
        WHERE ($1::uuid IS NULL OR actor_id = $1)
          AND ($2::text IS NULL OR entity_type = $2)
          AND ($3::text IS NULL OR action = $3)
          AND ($4::timestamptz IS NULL OR created_at >= $4)
          AND ($5::timestamptz IS NULL OR created_at <= $5)
        ORDER BY created_at DESC
        LIMIT $6 OFFSET $7
        "#
    )
    .bind(actor_id)
    .bind(if entity_type.is_empty() { None } else { Some(entity_type) })
    .bind(if action.is_empty() { None } else { Some(action) })
    .bind(from_dt)
    .bind(to_dt)
    .bind(limit as i64)
    .bind(offset as i64)
    .fetch_all(&state.pool)
    .await;

    match rows {
        Ok(data) => {
            let total_pages = if total == 0 { 1 } else { (total + limit - 1) / limit };
            (
                StatusCode::OK,
                Json(PaginatedResponse {
                    data,
                    total,
                    page,
                    total_pages,
                }),
            )
                .into_response()
        }
        Err(e) => {
            tracing::error!("Failed to fetch activity logs: {}", e);
            (
                StatusCode::INTERNAL_SERVER_ERROR,
                Json(serde_json::json!({ "error": "Failed to fetch activity logs" })),
            )
                .into_response()
        }
    }
}
