use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use axum::Json;
use fts_index::protocol::ApiResponse;
use log::error;
use thiserror::Error;
use tokio::time::Instant;

pub(crate) type Result<R> = std::result::Result<R, ServingError>;

#[derive(Error, Debug)]
pub(crate) enum ServingError {
    #[error("Internal server error")]
    InternalServerError,
    #[error("Internal server error: {0}")]
    InternalServerErrorWithMsg(&'static str),
}

pub(crate) fn internal_server_error<I, T>(msg: &'static str, log: I) -> ServingError
where
    I: Into<Option<T>>,
    T: ToString,
{
    if let Some(log) = log.into() {
        error!("{}", log.to_string());
    }
    if msg.is_empty() {
        ServingError::InternalServerError
    } else {
        ServingError::InternalServerErrorWithMsg(msg)
    }
}

impl IntoResponse for ServingError {
    fn into_response(self) -> Response {
        let status_code = match &self {
            ServingError::InternalServerError => StatusCode::INTERNAL_SERVER_ERROR,
            ServingError::InternalServerErrorWithMsg(_) => StatusCode::INTERNAL_SERVER_ERROR,
        };
        (
            status_code,
            Json(ApiResponse::<String>::new(Instant::now(), self.to_string())),
        )
            .into_response()
    }
}

impl From<serde_json::Error> for ServingError {
    fn from(value: serde_json::Error) -> Self {
        internal_server_error("", value)
    }
}

impl From<anyhow::Error> for ServingError {
    fn from(value: anyhow::Error) -> Self {
        internal_server_error("", value)
    }
}
