use crate::{
  AppState,
  auth::AdminAuthorization,
  error::{Error, Result},
};
use axum::{
  Json,
  Router,
  extract::Path,
  http::StatusCode,
  routing::{delete, get, put},
};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use siren_core::data;
use std::sync::Arc;
use uuid::Uuid;

pub fn get_routes() -> Router<Arc<AppState>> {
  Router::new()
    .route("/users", get(list_users))
    .route("/users/{id}/role", put(set_user_role))
    .route("/users/{id}/ban", put(ban_user))
    .route("/users/{id}/unban", put(unban_user))
    .route("/users/{id}", delete(delete_user))
}

/// Minimal user record returned by the admin list endpoint.
#[derive(Serialize, sqlx::FromRow)]
pub struct AdminUserRecord {
  pub id: String,
  pub username: String,
  pub email: Option<String>,
  pub role: String,
  pub status: String,
  pub created_at: DateTime<Utc>,
}

#[derive(sqlx::FromRow)]
struct DbAdminUser {
  id: Uuid,
  username: String,
  email: Option<String>,
  role: String,
  status: String,
  created_at: DateTime<Utc>,
}

#[derive(Deserialize)]
struct SetRolePayload {
  role: String,
}

/// `GET /admin/users` — list all user accounts (admin only).
async fn list_users(
  AdminAuthorization(_session): AdminAuthorization,
) -> Result<Json<Vec<AdminUserRecord>>> {
  let pool = data::pool();

  let rows: Vec<DbAdminUser> = sqlx::query_as(
    "SELECT id, username, email, role, status, created_at \
     FROM users \
     ORDER BY created_at ASC",
  )
  .fetch_all(pool)
  .await?;

  let records = rows
    .into_iter()
    .map(|r| AdminUserRecord {
      id: r.id.to_string(),
      username: r.username,
      email: r.email,
      role: r.role,
      status: r.status,
      created_at: r.created_at,
    })
    .collect();

  Ok(Json(records))
}

/// `PUT /admin/users/{id}/role` — promote or demote a user (admin only).
async fn set_user_role(
  AdminAuthorization(session): AdminAuthorization,
  Path(id): Path<Uuid>,
  Json(payload): Json<SetRolePayload>,
) -> Result<StatusCode> {
  if payload.role != "admin" && payload.role != "user" {
    return Err(Error::new(422, "role must be 'admin' or 'user'".into()));
  }

  // Prevent an admin from demoting themselves
  if id == session.user_id && payload.role != "admin" {
    return Err(Error::new(
      422,
      "You cannot remove your own admin role".into(),
    ));
  }

  let pool = data::pool();

  let affected = sqlx::query("UPDATE users SET role = $1, updated_at = NOW() WHERE id = $2")
    .bind(&payload.role)
    .bind(id)
    .execute(pool)
    .await?
    .rows_affected();

  if affected == 0 {
    return Err(Error::not_found(format!("User {id} not found")));
  }

  Ok(StatusCode::NO_CONTENT)
}

/// `PUT /admin/users/{id}/ban` — ban a user account (admin only).
async fn ban_user(
  AdminAuthorization(session): AdminAuthorization,
  Path(id): Path<Uuid>,
) -> Result<StatusCode> {
  // Admins cannot ban themselves
  if id == session.user_id {
    return Err(Error::new(422, "You cannot ban yourself".into()));
  }

  let pool = data::pool();

  let affected =
    sqlx::query("UPDATE users SET status = 'banned', updated_at = NOW() WHERE id = $1")
      .bind(id)
      .execute(pool)
      .await?
      .rows_affected();

  if affected == 0 {
    return Err(Error::not_found(format!("User {id} not found")));
  }

  Ok(StatusCode::NO_CONTENT)
}

/// `PUT /admin/users/{id}/unban` — reinstate a banned user (admin only).
async fn unban_user(
  AdminAuthorization(_session): AdminAuthorization,
  Path(id): Path<Uuid>,
) -> Result<StatusCode> {
  let pool = data::pool();

  let affected =
    sqlx::query("UPDATE users SET status = 'active', updated_at = NOW() WHERE id = $1")
      .bind(id)
      .execute(pool)
      .await?
      .rows_affected();

  if affected == 0 {
    return Err(Error::not_found(format!("User {id} not found")));
  }

  Ok(StatusCode::NO_CONTENT)
}

/// `DELETE /admin/users/{id}` — permanently delete a user account (admin only).
async fn delete_user(
  AdminAuthorization(session): AdminAuthorization,
  Path(id): Path<Uuid>,
) -> Result<StatusCode> {
  // Admins cannot delete themselves
  if id == session.user_id {
    return Err(Error::new(
      422,
      "You cannot delete your own account via the admin panel".into(),
    ));
  }

  let pool = data::pool();

  let affected = sqlx::query("DELETE FROM users WHERE id = $1")
    .bind(id)
    .execute(pool)
    .await?
    .rows_affected();

  if affected == 0 {
    return Err(Error::not_found(format!("User {id} not found")));
  }

  Ok(StatusCode::NO_CONTENT)
}
