use std::fmt;

use atuin_domain::api::{ErrorResponse, IndexResponse};
use axum::extract::State;
use axum::response::IntoResponse;
use axum::{Json, http};
use tracing::instrument;

use crate::router::AppState;

pub mod health;
pub mod user;
pub mod v0;

const VERSION: &str = env!("CARGO_PKG_VERSION");

#[instrument(skip_all)]
pub async fn index(state: State<AppState>) -> Json<IndexResponse> {
    let homage = r#""Through the fathomless deeps of space swims the star turtle Great A'Tuin, bearing on its back the four giant elephants who carry on their shoulders the mass of the Discworld." -- Sir Terry Pratchett"#;

    let version = state.settings.fake_version.clone().unwrap_or(VERSION.to_string());

    Json(IndexResponse {
        homage: homage.to_string(),
        version,
    })
}

impl fmt::Display for ErrorResponseStatus<'_> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "status={} reason={}", self.status, self.error.reason)
    }
}

impl IntoResponse for ErrorResponseStatus<'_> {
    fn into_response(self) -> axum::response::Response {
        (self.status, Json(self.error)).into_response()
    }
}

pub struct ErrorResponseStatus<'a> {
    pub error: ErrorResponse<'a>,
    pub status: http::StatusCode,
}

pub trait RespExt<'a> {
    fn with_status(self, status: http::StatusCode) -> ErrorResponseStatus<'a>;
    fn reply(reason: &'a str) -> Self;
}

impl<'a> RespExt<'a> for ErrorResponse<'a> {
    fn with_status(self, status: http::StatusCode) -> ErrorResponseStatus<'a> {
        ErrorResponseStatus {
            error: self,
            status,
        }
    }

    fn reply(reason: &'a str) -> Self {
        Self {
            reason: reason.into(),
        }
    }
}
