use crate::RouterApi;
use axum::body::Body;
use axum::extract::{Extension, Path};
use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use axum::routing::get;
use axum::{Json, Router};
use etwin_core::auth::AuthContext;
use etwin_core::dinoparc::{
  DinoparcDinozId, DinoparcServer, DinoparcUserId, EtwinDinoparcDinoz, EtwinDinoparcUser, GetDinoparcDinozOptions,
  GetDinoparcUserOptions,
};
use serde::Serialize;
use serde_json::json;

pub fn router() -> Router<Body> {
  Router::new()
    .route("/:server/users/:user_id", get(get_user))
    .route("/:server/dinoz/:user_id", get(get_dinoz))
}

#[derive(Copy, Clone, Debug, Serialize)]
#[serde(tag = "error")]
enum GetDinoparcUserError {
  DinoparcUserNotFound,
  InternalServerError,
}

impl IntoResponse for GetDinoparcUserError {
  fn into_response(self) -> Response {
    let status = match self {
      Self::DinoparcUserNotFound => StatusCode::NOT_FOUND,
      Self::InternalServerError => StatusCode::INTERNAL_SERVER_ERROR,
    };
    let (status, body) = match serde_json::to_value(self) {
      Ok(body) => (status, Json(body)),
      Err(_) => (
        StatusCode::INTERNAL_SERVER_ERROR,
        Json(json!({"error": "InternalServerError"})),
      ),
    };
    (status, body).into_response()
  }
}

async fn get_user(
  Extension(api): Extension<RouterApi>,
  Path((server, id)): Path<(DinoparcServer, DinoparcUserId)>,
) -> Result<Json<EtwinDinoparcUser>, GetDinoparcUserError> {
  let acx = AuthContext::guest();
  match api
    .dinoparc
    .get_user(&acx, &GetDinoparcUserOptions { server, id, time: None })
    .await
  {
    Ok(Some(result)) => Ok(Json(result)),
    Ok(None) => Err(GetDinoparcUserError::DinoparcUserNotFound),
    Err(_) => Err(GetDinoparcUserError::InternalServerError),
  }
}

#[derive(Copy, Clone, Debug, Serialize)]
#[serde(tag = "error")]
enum GetDinoparcDinozError {
  DinoparcDinozNotFound,
  InternalServerError,
}

impl IntoResponse for GetDinoparcDinozError {
  fn into_response(self) -> Response {
    let status = match self {
      Self::DinoparcDinozNotFound => StatusCode::NOT_FOUND,
      Self::InternalServerError => StatusCode::INTERNAL_SERVER_ERROR,
    };
    let (status, body) = match serde_json::to_value(self) {
      Ok(body) => (status, Json(body)),
      Err(_) => (
        StatusCode::INTERNAL_SERVER_ERROR,
        Json(json!({"error": "InternalServerError"})),
      ),
    };
    (status, body).into_response()
  }
}

async fn get_dinoz(
  Extension(api): Extension<RouterApi>,
  Path((server, id)): Path<(DinoparcServer, DinoparcDinozId)>,
) -> Result<Json<EtwinDinoparcDinoz>, GetDinoparcDinozError> {
  let acx = AuthContext::guest();
  match api
    .dinoparc
    .get_dinoz(&acx, &GetDinoparcDinozOptions { server, id, time: None })
    .await
  {
    Ok(Some(result)) => Ok(Json(result)),
    Ok(None) => Err(GetDinoparcDinozError::DinoparcDinozNotFound),
    Err(_) => Err(GetDinoparcDinozError::InternalServerError),
  }
}
