use std::fmt;

#[derive(Debug)]
pub enum AppError {
    UserNotFound,
    RsaGenerationError,
    RsaDecodeError,
    CaptchaError,
    TwoFactorRequired,
    Unauthorized,
    DoMainNotSet,
    UserNameExists,
    EmailExists,
    AccountAlreadyExists,
    TxnError,
    PasswordHashError(String),
    TwoFactorAlreadyEnabled,
    TwoFactorNotSetup,
    InvalidTwoFactorCode,
    TwoFactorNotEnabled,
    DatabaseError(String),
    InvalidPassword,
    PasswordTooWeak,
    ProjectNotFound,
    NoPower,
    InternalError,
    NotFound(String),
    RoleParseError,
    ProjectNameAlreadyExists,
    RepoNameAlreadyExists,
    AvatarUploadError(String),
    InternalServerError(String),
    PermissionDenied,
    RepoNotFound,
    RepoForBidAccess,
    SerdeError(serde_json::Error),
    Io(std::io::Error),
    BadRequest(String),
    Forbidden(String),
    Conflict(String),
    InvalidResetToken,
    ResetTokenExpired,
    ResetTokenUsed,
    IssueNotFound,
    LabelNotFound,
    MilestoneNotFound,
    PullRequestNotFound,
    CommentNotFound,
    GitRpcError(String),
    AiError(ai::error::AiError),
}

impl fmt::Display for AppError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            AppError::UserNotFound => write!(f, "user not found"),
            AppError::RsaGenerationError => {
                write!(f, "RSA key generation failed")
            }
            AppError::RsaDecodeError => write!(f, "RSA decode failed"),
            AppError::CaptchaError => write!(f, "captcha verification failed"),
            AppError::TwoFactorRequired => {
                write!(f, "two-factor authentication required")
            }
            AppError::Unauthorized => write!(f, "unauthorized"),
            AppError::DoMainNotSet => write!(f, "domain not configured"),
            AppError::UserNameExists => write!(f, "username already exists"),
            AppError::EmailExists => write!(f, "email already exists"),
            AppError::AccountAlreadyExists => {
                write!(f, "account already exists")
            }
            AppError::TxnError => write!(f, "transaction error"),
            AppError::PasswordHashError(e) => {
                write!(f, "password hash error: {}", e)
            }
            AppError::TwoFactorAlreadyEnabled => {
                write!(f, "two-factor already enabled")
            }
            AppError::TwoFactorNotSetup => write!(f, "two-factor not setup"),
            AppError::InvalidTwoFactorCode => {
                write!(f, "invalid two-factor code")
            }
            AppError::TwoFactorNotEnabled => {
                write!(f, "two-factor not enabled")
            }
            AppError::DatabaseError(e) => write!(f, "database error: {}", e),
            AppError::InvalidPassword => write!(f, "invalid password"),
            AppError::PasswordTooWeak => write!(f, "password too weak"),
            AppError::ProjectNotFound => write!(f, "project not found"),
            AppError::NoPower => write!(f, "permission denied"),
            AppError::InternalError => write!(f, "internal error"),
            AppError::NotFound(msg) => write!(f, "not found: {}", msg),
            AppError::RoleParseError => write!(f, "role parse error"),
            AppError::ProjectNameAlreadyExists => {
                write!(f, "project name already exists")
            }
            AppError::RepoNameAlreadyExists => {
                write!(f, "repo name already exists")
            }
            AppError::AvatarUploadError(e) => {
                write!(f, "avatar upload error: {}", e)
            }
            AppError::InternalServerError(e) => {
                write!(f, "internal server error: {}", e)
            }
            AppError::PermissionDenied => write!(f, "permission denied"),
            AppError::RepoNotFound => write!(f, "repo not found"),
            AppError::RepoForBidAccess => write!(f, "repo access forbidden"),
            AppError::SerdeError(e) => write!(f, "serde error: {}", e),
            AppError::Io(e) => write!(f, "IO error: {}", e),
            AppError::BadRequest(msg) => write!(f, "bad request: {}", msg),
            AppError::Forbidden(msg) => write!(f, "forbidden: {}", msg),
            AppError::Conflict(msg) => write!(f, "conflict: {}", msg),
            AppError::InvalidResetToken => write!(f, "invalid reset token"),
            AppError::ResetTokenExpired => write!(f, "reset token expired"),
            AppError::ResetTokenUsed => write!(f, "reset token already used"),
            AppError::IssueNotFound => write!(f, "issue not found"),
            AppError::LabelNotFound => write!(f, "label not found"),
            AppError::MilestoneNotFound => write!(f, "milestone not found"),
            AppError::PullRequestNotFound => {
                write!(f, "pull request not found")
            }
            AppError::CommentNotFound => write!(f, "comment not found"),
            AppError::GitRpcError(e) => write!(f, "git rpc error: {}", e),
            AppError::AiError(e) => write!(f, "ai error: {}", e),
        }
    }
}

impl std::error::Error for AppError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match self {
            AppError::SerdeError(e) => Some(e),
            AppError::Io(e) => Some(e),
            AppError::AiError(e) => Some(e),
            _ => None,
        }
    }
}

impl From<ai::error::AiError> for AppError {
    fn from(e: ai::error::AiError) -> Self {
        AppError::AiError(e)
    }
}
