use actix_web::{HttpResponse, error::ResponseError, http::StatusCode};
use serde::Serialize;
use service::error::AppError;

pub fn ok_json<T: Serialize>(data: T) -> Result<HttpResponse, ApiError> {
    Ok(HttpResponse::Ok().json(data))
}

pub struct ApiError(pub AppError);

impl From<AppError> for ApiError {
    fn from(err: AppError) -> Self {
        ApiError(err)
    }
}

impl std::fmt::Display for ApiError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.0.fmt(f)
    }
}

impl std::fmt::Debug for ApiError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.0.fmt(f)
    }
}

impl ResponseError for ApiError {
    fn status_code(&self) -> StatusCode {
        match &self.0 {
            AppError::Unauthorized => StatusCode::UNAUTHORIZED,
            AppError::UserNotFound => StatusCode::NOT_FOUND,
            AppError::InvalidPassword => StatusCode::UNAUTHORIZED,
            AppError::PasswordTooWeak => StatusCode::BAD_REQUEST,
            AppError::CaptchaError => StatusCode::BAD_REQUEST,
            AppError::TwoFactorRequired => {
                StatusCode::from_u16(402).unwrap_or(StatusCode::BAD_REQUEST)
            }
            AppError::TwoFactorAlreadyEnabled => StatusCode::CONFLICT,
            AppError::TwoFactorNotSetup => StatusCode::NOT_FOUND,
            AppError::InvalidTwoFactorCode => StatusCode::BAD_REQUEST,
            AppError::TwoFactorNotEnabled => StatusCode::NOT_FOUND,
            AppError::RsaGenerationError => StatusCode::INTERNAL_SERVER_ERROR,
            AppError::RsaDecodeError => StatusCode::BAD_REQUEST,
            AppError::UserNameExists => StatusCode::CONFLICT,
            AppError::EmailExists => StatusCode::CONFLICT,
            AppError::AccountAlreadyExists => StatusCode::CONFLICT,
            AppError::TxnError => StatusCode::INTERNAL_SERVER_ERROR,
            AppError::PasswordHashError(_) => StatusCode::INTERNAL_SERVER_ERROR,
            AppError::DatabaseError(_) => StatusCode::INTERNAL_SERVER_ERROR,
            AppError::DoMainNotSet => StatusCode::INTERNAL_SERVER_ERROR,
            AppError::InternalError => StatusCode::INTERNAL_SERVER_ERROR,
            AppError::InternalServerError(_) => {
                StatusCode::INTERNAL_SERVER_ERROR
            }
            AppError::PermissionDenied => StatusCode::FORBIDDEN,
            AppError::ProjectNotFound => StatusCode::NOT_FOUND,
            AppError::NoPower => StatusCode::FORBIDDEN,
            AppError::RoleParseError => StatusCode::BAD_REQUEST,
            AppError::ProjectNameAlreadyExists => StatusCode::CONFLICT,
            AppError::RepoNameAlreadyExists => StatusCode::CONFLICT,
            AppError::AvatarUploadError(_) => StatusCode::BAD_REQUEST,
            AppError::RepoNotFound => StatusCode::NOT_FOUND,
            AppError::RepoForBidAccess => StatusCode::FORBIDDEN,
            AppError::SerdeError(_) => StatusCode::BAD_REQUEST,
            AppError::Io(_) => StatusCode::INTERNAL_SERVER_ERROR,
            AppError::BadRequest(_) => StatusCode::BAD_REQUEST,
            AppError::Forbidden(_) => StatusCode::FORBIDDEN,
            AppError::Conflict(_) => StatusCode::CONFLICT,
            AppError::NotFound(_) => StatusCode::NOT_FOUND,
            AppError::InvalidResetToken => StatusCode::BAD_REQUEST,
            AppError::ResetTokenExpired => StatusCode::BAD_REQUEST,
            AppError::ResetTokenUsed => StatusCode::BAD_REQUEST,
            AppError::IssueNotFound => StatusCode::NOT_FOUND,
            AppError::LabelNotFound => StatusCode::NOT_FOUND,
            AppError::MilestoneNotFound => StatusCode::NOT_FOUND,
            AppError::PullRequestNotFound => StatusCode::NOT_FOUND,
            AppError::CommentNotFound => StatusCode::NOT_FOUND,
            AppError::GitRpcError(_) => StatusCode::INTERNAL_SERVER_ERROR,
            AppError::AiError(_) => StatusCode::INTERNAL_SERVER_ERROR,
        }
    }

    fn error_response(&self) -> HttpResponse {
        let status = self.status_code();
        let message = self.0.to_string();
        HttpResponse::build(status)
            .json(serde_json::json!({ "error": message }))
    }
}
