package auth

import (
	
	
	

	

	
	
	
)

// PasswordHash computes password hash to log in.
//
// See https://core.telegram.org/api/srp#checking-the-password-with-srp.
func (
	 []byte,
	 int64,
	,  []byte,
	 tg.PasswordKdfAlgoClass,
) (*tg.InputCheckPasswordSRP, error) {
	 := srp.NewSRP(crypto.DefaultRand())

	,  := .(*tg.PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow)
	if ! {
		return nil, errors.Errorf("unsupported algo: %T", )
	}

	,  := .Hash(, , , srp.Input(*))
	if  != nil {
		return nil, errors.Wrap(, "create SRP answer")
	}

	return &tg.InputCheckPasswordSRP{
		SRPID: ,
		A:     .A,
		M1:    .M1,
	}, nil
}

// NewPasswordHash computes new password hash to update password.
//
// Notice that NewPasswordHash mutates given alg.
//
// See https://core.telegram.org/api/srp#setting-a-new-2fa-password.
func (
	 []byte,
	 *tg.PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow,
) ( []byte,  error) {
	 := srp.NewSRP(crypto.DefaultRand())

	, ,  := .NewHash(, srp.Input(*))
	if  != nil {
		return nil, errors.Wrap(, "create SRP answer")
	}
	.Salt1 = 

	return , nil
}

var (
	emptyPassword tg.InputCheckPasswordSRPClass = &tg.InputCheckPasswordEmpty{}
)

// UpdatePasswordOptions is options structure for UpdatePassword.
type UpdatePasswordOptions struct {
	// Hint is new password hint.
	Hint string
	// Password is password callback.
	//
	// If password was requested and Password is nil, ErrPasswordNotProvided error will be returned.
	Password func(ctx context.Context) (string, error)
}

// UpdatePassword sets new cloud password for this account.
//
// See https://core.telegram.org/api/srp#setting-a-new-2fa-password.
func ( *Client) (
	 context.Context,
	 string,
	 UpdatePasswordOptions,
) error {
	,  := .api.AccountGetPassword()
	if  != nil {
		return errors.Wrap(, "get SRP parameters")
	}

	,  := .NewAlgo.(*tg.PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow)
	if ! {
		return errors.Errorf("unsupported algo: %T", .NewAlgo)
	}

	,  := NewPasswordHash([]byte(), )
	if  != nil {
		return errors.Wrap(, "compute new password hash")
	}

	var  = emptyPassword
	if .HasPassword {
		if .Password == nil {
			return ErrPasswordNotProvided
		}

		,  := .Password()
		if  != nil {
			return errors.Wrap(, "get password")
		}

		,  := PasswordHash([]byte(), .SRPID, .SRPB, .SecureRandom, .CurrentAlgo)
		if  != nil {
			return errors.Wrap(, "compute old password hash")
		}
		 = 
	}

	if ,  := .api.AccountUpdatePasswordSettings(, &tg.AccountUpdatePasswordSettingsRequest{
		Password: ,
		NewSettings: tg.AccountPasswordInputSettings{
			NewAlgo:         ,
			NewPasswordHash: ,
			Hint:            .Hint,
		},
	});  != nil {
		return errors.Wrap(, "update password")
	}
	return nil
}

// ResetFailedWaitError reports that you recently requested a password reset that was cancel and need to wait until the
// specified date before requesting another reset.
type ResetFailedWaitError struct {
	Result tg.AccountResetPasswordFailedWait
}

// Until returns time required to wait.
func ( ResetFailedWaitError) () time.Duration {
	 := time.Unix(int64(.Result.RetryDate), 0)
	return time.Until()
}

// Error implements error.
func ( *ResetFailedWaitError) () string {
	return fmt.Sprintf("wait to reset password (%s)", .Until())
}

// ResetPassword resets cloud password and returns time to wait until reset be performed.
// If time is zero, password was successfully reset.
//
// May return ResetFailedWaitError.
//
// See https://core.telegram.org/api/srp#password-reset.
func ( *Client) ( context.Context) (time.Time, error) {
	,  := .api.AccountResetPassword()
	if  != nil {
		return time.Time{}, errors.Wrap(, "reset password")
	}
	switch v := .(type) {
	case *tg.AccountResetPasswordFailedWait:
		return time.Time{}, &ResetFailedWaitError{Result: *}
	case *tg.AccountResetPasswordRequestedWait:
		return time.Unix(int64(.UntilDate), 0), nil
	case *tg.AccountResetPasswordOk:
		return time.Time{}, nil
	default:
		return time.Time{}, errors.Errorf("unexpected type %T", )
	}
}

// CancelPasswordReset cancels password reset.
//
// See https://core.telegram.org/api/srp#password-reset.
func ( *Client) ( context.Context) error {
	if ,  := .api.AccountDeclinePasswordReset();  != nil {
		return errors.Wrap(, "cancel password reset")
	}
	return nil
}

// RequestPasswordRecovery requests a recovery code to be sent to the recovery
// email set up for the 2FA password and returns the pattern of that email.
//
// The returned code is used by RecoverPassword and CheckRecoveryPassword.
//
// See https://core.telegram.org/method/auth.requestPasswordRecovery.
func ( *Client) ( context.Context) (*tg.AuthPasswordRecovery, error) {
	,  := .api.AuthRequestPasswordRecovery()
	if  != nil {
		return nil, errors.Wrap(, "request password recovery")
	}
	return , nil
}

// CheckRecoveryPassword checks whether the recovery code sent to the recovery
// email (see RequestPasswordRecovery) is valid, without resetting the password.
//
// See https://core.telegram.org/method/auth.checkRecoveryPassword.
func ( *Client) ( context.Context,  string) (bool, error) {
	,  := .api.AuthCheckRecoveryPassword(, )
	if  != nil {
		return false, errors.Wrap(, "check recovery password")
	}
	return , nil
}

// RecoverPasswordOptions is options structure for RecoverPassword.
type RecoverPasswordOptions struct {
	// Code is the recovery code received via the recovery email.
	//
	// Use RequestPasswordRecovery to send a recovery code to the email.
	Code string
	// NewPassword, if not empty, sets a new 2FA password after recovery.
	//
	// If empty, the 2FA password is removed.
	NewPassword string
	// Hint is the new password hint.
	//
	// Used only if NewPassword is not empty.
	Hint string
}

// RecoverPassword resets the 2FA password using the recovery code sent to the
// recovery email (see RequestPasswordRecovery) and logs in.
//
// If opts.NewPassword is set, it becomes the new 2FA password, otherwise the
// password is removed.
//
// See https://core.telegram.org/method/auth.recoverPassword.
func ( *Client) ( context.Context,  RecoverPasswordOptions) (*tg.AuthAuthorization, error) {
	 := &tg.AuthRecoverPasswordRequest{
		Code: .Code,
	}

	if .NewPassword != "" {
		,  := .api.AccountGetPassword()
		if  != nil {
			return nil, errors.Wrap(, "get SRP parameters")
		}

		,  := .NewAlgo.(*tg.PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow)
		if ! {
			return nil, errors.Errorf("unsupported algo: %T", .NewAlgo)
		}

		,  := NewPasswordHash([]byte(.NewPassword), )
		if  != nil {
			return nil, errors.Wrap(, "compute new password hash")
		}

		.SetNewSettings(tg.AccountPasswordInputSettings{
			NewAlgo:         ,
			NewPasswordHash: ,
			Hint:            .Hint,
		})
	}

	,  := .api.AuthRecoverPassword(, )
	if  != nil {
		return nil, errors.Wrap(, "recover password")
	}

	,  := checkResult()
	if  != nil {
		return nil, errors.Wrap(, "check")
	}
	return , nil
}