use git::GitError;

#[derive(Debug)]
pub enum AppError {
    UserNotFound,
    RsaGenerationError,
    RsaDecodeError,
    CaptchaError,
    TwoFactorRequired,
    Unauthorized,
    DoMainNotSet,
    UserNameExists,
    EmailExists,
    TxnError,
    PasswordHashError(String),
    TwoFactorAlreadyEnabled,
    TwoFactorNotSetup,
    InvalidTwoFactorCode,
    TwoFactorNotEnabled,
    DatabaseError(String),
    InvalidPassword,
    ProjectNotFound,
    NoPower,
    InternalError,
    NotFound(String),
    RoleParseError,
    ProjectNameAlreadyExists,
    RepoNameAlreadyExists,
    AvatarUploadError(String),
    InternalServerError(String),
    PermissionDenied,
    RepoNotFound,
    RepoForBidAccess,
    GitError(GitError),
    SerdeError(serde_json::Error),
    Io(std::io::Error),
    BadRequest(String),
    Forbidden(String),
    WorkspaceNotFound,
    WorkspaceSlugAlreadyExists,
    WorkspaceNameAlreadyExists,
    NotWorkspaceMember,
    WorkspaceInviteTokenInvalid,
    WorkspaceInviteExpired,
    WorkspaceInviteAlreadyAccepted,
    Conflict(String),
}

impl AppError {
    pub const fn code(&self) -> i32 {
        use AppError::*;
        match self {
            BadRequest(_) => 40000,
            CaptchaError => 40001,
            SerdeError(_) => 40002,
            RsaDecodeError => 40003,
            RoleParseError => 40004,
            TwoFactorNotSetup => 40005,
            TwoFactorNotEnabled => 40006,
            Unauthorized => 40101,
            InvalidTwoFactorCode => 40102,
            InvalidPassword => 40103,
            NoPower => 40301,
            PermissionDenied => 40302,
            Forbidden(_) => 40304,
            RepoForBidAccess => 40303,
            NotFound(_) => 40401,
            UserNotFound => 40402,
            ProjectNotFound => 40403,
            RepoNotFound => 40404,
            UserNameExists => 40901,
            EmailExists => 40902,
            ProjectNameAlreadyExists => 40903,
            RepoNameAlreadyExists => 40905,
            TwoFactorAlreadyEnabled => 40904,
            TwoFactorRequired => 42801,
            DoMainNotSet => 50001,
            TxnError => 50002,
            RsaGenerationError => 50003,
            PasswordHashError(_) => 50004,
            DatabaseError(_) => 50005,
            GitError(_) => 50006,
            Io(_) => 50007,
            InternalError => 50008,
            InternalServerError(_) => 50009,
            AvatarUploadError(_) => 50010,
            WorkspaceNotFound => 40405,
            WorkspaceSlugAlreadyExists => 40906,
            WorkspaceNameAlreadyExists => 40907,
            NotWorkspaceMember => 40305,
            WorkspaceInviteTokenInvalid => 40006,
            WorkspaceInviteExpired => 40007,
            WorkspaceInviteAlreadyAccepted => 40908,
            Conflict(_) => 40909,
        }
    }

    pub const fn http_status_code(&self) -> u16 {
        use AppError::*;
        match self {
            BadRequest(_) => 400,
            CaptchaError => 400,
            SerdeError(_) => 400,
            RsaDecodeError => 400,
            RoleParseError => 400,
            TwoFactorNotSetup => 400,
            TwoFactorNotEnabled => 400,
            WorkspaceInviteTokenInvalid => 400,
            WorkspaceInviteExpired => 400,
            Unauthorized => 401,
            InvalidTwoFactorCode => 401,
            InvalidPassword => 401,
            NoPower => 403,
            PermissionDenied => 403,
            Forbidden(_) => 403,
            RepoForBidAccess => 403,
            NotWorkspaceMember => 403,
            NotFound(_) => 404,
            UserNotFound => 404,
            ProjectNotFound => 404,
            RepoNotFound => 404,
            WorkspaceNotFound => 404,
            UserNameExists => 409,
            EmailExists => 409,
            ProjectNameAlreadyExists => 409,
            RepoNameAlreadyExists => 409,
            TwoFactorAlreadyEnabled => 409,
            WorkspaceSlugAlreadyExists => 409,
            WorkspaceNameAlreadyExists => 409,
            WorkspaceInviteAlreadyAccepted => 409,
            Conflict(_) => 409,
            TwoFactorRequired => 428,
            DoMainNotSet => 500,
            TxnError => 500,
            RsaGenerationError => 500,
            PasswordHashError(_) => 500,
            DatabaseError(_) => 500,
            GitError(_) => 500,
            Io(_) => 500,
            InternalError => 500,
            InternalServerError(_) => 500,
            AvatarUploadError(_) => 500,
        }
    }

    pub const fn slug(&self) -> &'static str {
        use AppError::*;
        match self {
            Unauthorized => "unauthorized",
            UserNotFound => "user_not_found",
            ProjectNotFound => "project_not_found",
            RepoNotFound => "repo_not_found",
            NotFound(_) => "not_found",
            TwoFactorRequired => "two_factor_required",
            UserNameExists => "username_exists",
            EmailExists => "email_exists",
            ProjectNameAlreadyExists => "project_name_exists",
            RepoNameAlreadyExists => "repo_name_exists",
            CaptchaError => "captcha_error",
            BadRequest(_) => "bad_request",
            SerdeError(_) => "serde_error",
            RsaDecodeError => "rsa_decode_error",
            RoleParseError => "role_parse_error",
            TwoFactorNotSetup => "two_factor_not_setup",
            TwoFactorNotEnabled => "two_factor_not_enabled",
            InvalidTwoFactorCode => "invalid_two_factor_code",
            InvalidPassword => "invalid_password",
            NoPower => "no_power",
            PermissionDenied => "permission_denied",
            Forbidden(_) => "forbidden",
            RepoForBidAccess => "repo_forbidden",
            TwoFactorAlreadyEnabled => "two_factor_already_enabled",
            WorkspaceNotFound => "workspace_not_found",
            WorkspaceSlugAlreadyExists => "workspace_slug_exists",
            WorkspaceNameAlreadyExists => "workspace_name_exists",
            NotWorkspaceMember => "not_workspace_member",
            WorkspaceInviteTokenInvalid => "workspace_invite_token_invalid",
            WorkspaceInviteExpired => "workspace_invite_expired",
            WorkspaceInviteAlreadyAccepted => "workspace_invite_already_accepted",
            Conflict(_) => "conflict",
            DoMainNotSet => "domain_not_set",
            TxnError => "transaction_error",
            RsaGenerationError => "rsa_generation_error",
            PasswordHashError(_) => "password_hash_error",
            DatabaseError(_) => "database_error",
            GitError(_) => "git_error",
            Io(_) => "io_error",
            InternalError => "internal_error",
            InternalServerError(_) => "internal_server_error",
            AvatarUploadError(_) => "avatar_upload_error",
        }
    }

    pub fn user_message(&self) -> String {
        match self {
            AppError::NotFound(s) => s.clone(),
            AppError::BadRequest(s) => s.clone(),
            AppError::Forbidden(s) => s.clone(),
            AppError::DatabaseError(_) => "A database error occurred".to_string(),
            AppError::PasswordHashError(_) => "A password processing error occurred".to_string(),
            AppError::GitError(_) => "A git operation failed".to_string(),
            AppError::SerdeError(_) => "A data parsing error occurred".to_string(),
            AppError::Io(_) => "A file system error occurred".to_string(),
            AppError::InternalServerError(_) => "An internal error occurred".to_string(),
            AppError::AvatarUploadError(_) => "An avatar upload error occurred".to_string(),
            AppError::Conflict(_) => "Resource conflict".to_string(),
            _ => self.slug().to_string(),
        }
    }
}

impl From<argon2::password_hash::Error> for AppError {
    fn from(value: argon2::password_hash::Error) -> Self {
        AppError::PasswordHashError(value.to_string())
    }
}

impl From<sea_orm::error::DbErr> for AppError {
    fn from(value: sea_orm::error::DbErr) -> Self {
        AppError::DatabaseError(value.to_string())
    }
}

impl From<GitError> for AppError {
    fn from(value: GitError) -> Self {
        AppError::GitError(value)
    }
}

impl From<serde_json::Error> for AppError {
    fn from(value: serde_json::Error) -> Self {
        AppError::SerdeError(value)
    }
}

impl From<std::io::Error> for AppError {
    fn from(value: std::io::Error) -> Self {
        AppError::Io(value)
    }
}

impl From<anyhow::Error> for AppError {
    fn from(value: anyhow::Error) -> Self {
        AppError::InternalServerError(value.to_string())
    }
}
impl From<room::RoomError> for AppError {
    fn from(err: room::RoomError) -> Self {
        use room::RoomError;
        match err {
            RoomError::Database(e) => {
                println!("database error: {}", e);
                AppError::DatabaseError(e.to_string())
            }
            RoomError::NotFound(s) => AppError::NotFound(s),
            RoomError::Unauthorized => AppError::Unauthorized,
            RoomError::NoPower => AppError::NoPower,
            RoomError::RateLimited(s) => AppError::BadRequest(s),
            RoomError::BadRequest(s) => AppError::BadRequest(s),
            RoomError::RoleParseError => AppError::RoleParseError,
            RoomError::Internal(s) => AppError::InternalServerError(s),
        }
    }
}
