use candid::CandidType;
use ex3_common_error_info::ErrorInfo;
use serde::Deserialize;

use crate::{AssetId, CanisterId, WalletRegisterId};

pub type ServiceResult<T> = anyhow::Result<T, ErrorInfo>;

/// When export_service, actor responses will merged by enum type, so if there is two response with same Ok type, the second response will be ignored.
/// So there is no need to create more than one response type for two boolean ok.
#[derive(CandidType, Deserialize, Debug, Clone)]
pub enum EmptyActorResponse {
    Ok(()),
    Err(ErrorInfo),
}

impl EmptyActorResponse {
    pub fn new(result: ServiceResult<()>) -> Self {
        match result {
            Ok(()) => EmptyActorResponse::Ok(()),
            Err(err) => EmptyActorResponse::Err(err.into()),
        }
    }
}

/// When export_service, actor responses will merged by enum type, so if there is two response with same Ok type, the second response will be ignored.
/// So there is no need to create more than one response type for two boolean ok.
#[derive(CandidType, Deserialize, Debug)]
pub enum BooleanActorResponse {
    Ok(bool),
    Err(ErrorInfo),
}

impl BooleanActorResponse {
    pub fn new(result: ServiceResult<bool>) -> BooleanActorResponse {
        match result {
            Ok(available) => BooleanActorResponse::Ok(available),
            Err(err) => BooleanActorResponse::Err(err.into()),
        }
    }
}

#[derive(CandidType)]
pub enum StringActorResponse {
    Ok(String),
    Err(ErrorInfo),
}

impl StringActorResponse {
    pub fn new(result: ServiceResult<String>) -> StringActorResponse {
        match result {
            Ok(available) => StringActorResponse::Ok(available),
            Err(err) => StringActorResponse::Err(err.into()),
        }
    }
}

/// When export_service, actor responses will merged by enum type, so if there is two response with same Ok type, the second response will be ignored.
/// So there is no need to create more than one response type for two boolean ok.
#[derive(CandidType, Deserialize, Debug)]
pub enum CanisterIdActorResponse {
    Ok(CanisterId),
    Err(ErrorInfo),
}

impl CanisterIdActorResponse {
    pub fn new(result: ServiceResult<CanisterId>) -> CanisterIdActorResponse {
        match result {
            Ok(available) => CanisterIdActorResponse::Ok(available),
            Err(err) => CanisterIdActorResponse::Err(err.into()),
        }
    }
}

/// When export_service, actor responses will merged by enum type, so if there is two response with same Ok type, the second response will be ignored.
/// So there is no need to create more than one response type for two boolean ok.
#[derive(CandidType, Deserialize, Debug)]
pub enum OptionCanisterIdActorResponse {
    Ok(Option<CanisterId>),
    Err(ErrorInfo),
}

impl OptionCanisterIdActorResponse {
    pub fn new(result: ServiceResult<Option<CanisterId>>) -> OptionCanisterIdActorResponse {
        match result {
            Ok(c) => OptionCanisterIdActorResponse::Ok(c),
            Err(err) => OptionCanisterIdActorResponse::Err(err.into()),
        }
    }
}

/// When export_service, actor responses will merged by enum type, so if there is two response with same Ok type, the second response will be ignored.
/// So there is no need to create more than one response type for two boolean ok.
#[derive(CandidType)]
pub enum WalletRegisterIdActorResponse {
    Ok(WalletRegisterId),
    Err(ErrorInfo),
}

impl WalletRegisterIdActorResponse {
    pub fn new(result: ServiceResult<WalletRegisterId>) -> WalletRegisterIdActorResponse {
        match result {
            Ok(c) => WalletRegisterIdActorResponse::Ok(c),
            Err(err) => WalletRegisterIdActorResponse::Err(err.into()),
        }
    }
}
