package models

import (
	"fmt"
	"net/http"
)

var ErrMap = map[string]*CodedError{}
var codeErrs = []*CodedError{
	ErrDisplaynameNotAvailable,
	ErrDisplaynameTooShort,
	ErrDisplaynameTooLong,
	ErrDisplaynameInvalidCharacters,
	ErrDisplaynameChangeAgain,
	ErrDisplaynameOnlyCapitalization,
	ErrUnexpectedDisplaynameOverrideErr,
	ErrLoginNotAvailable,
	ErrLoginBlocked,
	ErrNotAllowedToChangeLogin,
	ErrLoginTooShort,
	ErrLoginTooLong,
	ErrLoginInvalidCharacters,
	ErrLoginUsingBannedWords,
	ErrLoginUsingVariationOnBannedWords,
	ErrPartnerDoNotSupportGuid,
	ErrPartnerNotReachable,
	ErrFilteredUser,
	ErrIPBlocked,
	TooManyUsersAssociatedWithEmail,
	ErrEmailDisabledForReuse,
	ErrNotAllowedToEnableEmailReuse,
	ErrInvalidEmail,
	ErrWorkEmailRequired,
	ErrInvalidEmailDomain,
	ErrVerifyCodeExpired,
	ErrVerifyDoesNotMatch,
	ErrInvalidPhoneNumber,
	ErrInvalidPhoneNumberChars,
	ErrInvalidGameID,
	ErrNotAllowedToHardDelete,
	ErrBannedUserChannels,
	ErrNoIdentifiers,
	ErrBadIdentifiers,
	ErrTooManyIdentifiers,
	ErrNoChannelIdentifiers,
	ErrBadChannelIdentifiers,
	ErrReservationAlreadyExist,
	ErrReservationNotExist,
	ErrDescriptionTooLong,
	ErrClientUnexpected,
	ErrTryToRollbackDifferentBlockRecord,
	ErrNotAllowedToDeletePhoneNumber,
	ErrInvalidParameterCombination,
	ErrPhoneNumberAlreadyExists,
	ErrInvalidURL,
	ErrNotAllowedToChangeEmail,
	ErrInvalidToken,
	ErrEmptyToken,
	ErrInvalidURL,
	ErrBannedWord,
	ErrRequestsThrottled,
	ErrAuthFailure,
	ErrGameTooLong,
	ErrGameProtected,
	ErrInvalidBirthday,
	ErrHexColorInvalid,
	ErrLocaleInvalid,
	ErrInvalidLastLogin,
	ErrStatusTooLong,
	ErrStatusUsingBannedWords,
	ErrSudoReauthNeeded,
	ErrUserNotEligible,
	ErrAdminLoginRequired,
}

var ErrDisplaynameNotAvailable = &CodedError{
	ErrorValue:      "The display name you selected is not available.",
	CodeValue:       "display_name_not_available",
	StatusCodeValue: http.StatusForbidden,
}

var ErrDisplaynameTooShort = &CodedError{
	ErrorValue:      "The name you selected is too short.",
	CodeValue:       "display_name_too_short",
	StatusCodeValue: http.StatusBadRequest,
}

var ErrDisplaynameTooLong = &CodedError{
	ErrorValue:      "The display name you selected is too long.",
	CodeValue:       "display_name_too_long",
	StatusCodeValue: http.StatusBadRequest,
}

var ErrDisplaynameInvalidCharacters = &CodedError{
	ErrorValue:      "The name you selected contains invalid characters.",
	CodeValue:       "invalid_chars_in_display_name",
	StatusCodeValue: http.StatusBadRequest,
}

var ErrDisplaynameChangeAgain = &CodedError{
	ErrorValue:      "You may not change your display name again.",
	CodeValue:       "display_name_change_again",
	StatusCodeValue: http.StatusTooManyRequests,
}

var ErrDisplaynameOnlyCapitalization = &CodedError{
	ErrorValue:      "You may not change your display name, only the capitalization of it.",
	CodeValue:       "display_name_only_cap",
	StatusCodeValue: http.StatusBadRequest,
}

var ErrUnexpectedDisplaynameOverrideErr = &CodedError{
	ErrorValue:      "Unexpected error during displayname override",
	CodeValue:       "display_name_unexpected_err",
	StatusCodeValue: http.StatusInternalServerError,
}

var ErrLoginNotAvailable = &CodedError{
	ErrorValue:      "The login you selected is not available.",
	CodeValue:       "login_not_available",
	StatusCodeValue: http.StatusForbidden,
}

var ErrLoginBlocked = &CodedError{
	ErrorValue:      "The login you selected is not yet available for re-use.",
	CodeValue:       "login_blocked",
	StatusCodeValue: http.StatusForbidden,
}

var ErrNotAllowedToChangeLogin = &CodedError{
	ErrorValue:      fmt.Sprintf("You are not allowed to change your login more than once every %d days.", LoginRenameCooldown),
	CodeValue:       "not_allowed_to_change_login",
	StatusCodeValue: http.StatusForbidden,
}

var ErrLoginTooShort = &CodedError{
	ErrorValue:      "The login you selected is too short.",
	CodeValue:       "login_too_short",
	StatusCodeValue: http.StatusBadRequest,
}

var ErrLoginTooLong = &CodedError{
	ErrorValue:      "The login you selected is too long.",
	CodeValue:       "login_too_long",
	StatusCodeValue: http.StatusBadRequest,
}

var ErrLoginInvalidCharacters = &CodedError{
	ErrorValue:      "The login you selected contains invalid characters.",
	CodeValue:       "invalid_chars_in_login",
	StatusCodeValue: http.StatusBadRequest,
}

var ErrLoginUsingBannedWords = &CodedError{
	ErrorValue:      "Login contains banned words.",
	CodeValue:       "login_use_banned_words",
	StatusCodeValue: http.StatusBadRequest,
}

var ErrLoginUsingVariationOnBannedWords = &CodedError{
	ErrorValue:      "Login contains variations on banned words.",
	CodeValue:       "login_use_variation_on_banned_words",
	StatusCodeValue: http.StatusBadRequest,
}

var ErrPartnerDoNotSupportGuid = &CodedError{
	ErrorValue:      "The partnership service does not support GUID user IDs",
	CodeValue:       "partner_not_support_guid",
	StatusCodeValue: http.StatusForbidden,
}

var ErrPartnerNotReachable = &CodedError{
	ErrorValue:      "The partnership service is unreachable",
	CodeValue:       "partner_not_reachable",
	StatusCodeValue: http.StatusForbidden,
}

var ErrFilteredUser = &CodedError{
	ErrorValue:      "User exists but was excluded by filter criteria.",
	CodeValue:       "filtered_user_requested",
	StatusCodeValue: http.StatusUnprocessableEntity,
}

var ErrIPBlocked = &CodedError{
	ErrorValue:      "The IP is blocked.",
	CodeValue:       "ip_blocked",
	StatusCodeValue: http.StatusUnprocessableEntity,
}

var TooManyUsersAssociatedWithEmail = &CodedError{
	ErrorValue:      "Too many users associated with the email.",
	CodeValue:       "too_many_users_for_email",
	StatusCodeValue: http.StatusUnprocessableEntity,
}

var ErrEmailDisabledForReuse = &CodedError{
	ErrorValue:      "Email disabled for reuse.",
	CodeValue:       "email_disabled_for_reuse",
	StatusCodeValue: http.StatusUnprocessableEntity,
}

var ErrNotAllowedToEnableEmailReuse = &CodedError{
	ErrorValue:      "Not allowed to enable email reuse.",
	CodeValue:       "not_allowed_to_enable_email_reuse",
	StatusCodeValue: http.StatusForbidden,
}

var ErrInvalidEmail = &CodedError{
	ErrorValue:      "Email address is not valid",
	CodeValue:       "invalid_email",
	StatusCodeValue: http.StatusBadRequest,
}

var ErrWorkEmailRequired = &CodedError{
	ErrorValue:      "Admins must use their work email",
	CodeValue:       "work_email_required",
	StatusCodeValue: http.StatusForbidden,
}

var ErrInvalidEmailDomain = &CodedError{
	ErrorValue:      "Email address uses an invalid domain",
	CodeValue:       "invalid_email_domain",
	StatusCodeValue: http.StatusBadRequest,
}

var ErrVerifyCodeExpired = &CodedError{
	ErrorValue:      "Verification code has expired",
	CodeValue:       "verify_code_expired",
	StatusCodeValue: http.StatusForbidden,
}

var ErrVerifyDoesNotMatch = &CodedError{
	ErrorValue:      "Verification code does not match",
	CodeValue:       "verify_code_does_not_match",
	StatusCodeValue: http.StatusBadRequest,
}

var ErrInvalidPhoneNumber = &CodedError{
	ErrorValue:      "Phone number is invalid",
	CodeValue:       "phone_number_invalid",
	StatusCodeValue: http.StatusBadRequest,
}

var ErrInvalidPhoneNumberChars = &CodedError{
	ErrorValue:      "phone number cannot contain letters",
	CodeValue:       "phone_number_has_letters",
	StatusCodeValue: http.StatusBadRequest,
}

var ErrNotAllowedToHardDelete = &CodedError{
	CodeValue:       "not_allowed_to_hard_delete",
	StatusCodeValue: http.StatusForbidden,
}

var ErrBannedUserChannels = &CodedError{
	ErrorValue:      "Channel is unavailable",
	CodeValue:       "channel_unavailable",
	StatusCodeValue: http.StatusUnprocessableEntity,
}

var ErrNoIdentifiers = &CodedError{
	ErrorValue:      "No login names, emails, phone numbers, IDs or display names in request",
	StatusCodeValue: http.StatusBadRequest,
}

var ErrBadIdentifiers = &CodedError{
	ErrorValue:      "Invalid login names, emails or IDs in request",
	CodeValue:       "bad_identifiers",
	StatusCodeValue: http.StatusBadRequest,
}

var ErrTooManyIdentifiers = &CodedError{
	ErrorValue:      "Too many identifiers in request",
	CodeValue:       "too_many_identifiers",
	StatusCodeValue: http.StatusBadRequest,
}

var ErrNoChannelIdentifiers = &CodedError{
	ErrorValue:      "No channel names or IDs in request",
	CodeValue:       "no_channel_identifiers",
	StatusCodeValue: http.StatusBadRequest,
}

var ErrBadChannelIdentifiers = &CodedError{
	ErrorValue:      "Invalid channel names or IDs in request",
	CodeValue:       "bad_channel_identifiers",
	StatusCodeValue: http.StatusBadRequest,
}

var ErrReservationAlreadyExist = &CodedError{
	ErrorValue:      "Reservation already in DB. Duplication",
	CodeValue:       "reservation_already_exits",
	StatusCodeValue: http.StatusBadRequest,
}

var ErrReservationNotExist = &CodedError{
	ErrorValue:      "Reservation not exist in DB",
	CodeValue:       "reservation_not_exits",
	StatusCodeValue: http.StatusBadRequest,
}

var ErrDescriptionTooLong = &CodedError{
	ErrorValue:      "Description field is too long",
	CodeValue:       "description_too_long",
	StatusCodeValue: http.StatusRequestEntityTooLarge,
}

var ErrClientUnexpected = &CodedError{
	ErrorValue:      "Unexpected client error",
	CodeValue:       "unexpected_client_error",
	StatusCodeValue: http.StatusFailedDependency,
}

var ErrTryToRollbackDifferentBlockRecord = &CodedError{
	ErrorValue: "Try to rollback different block record",
	CodeValue:  "rollback_different_block_record",
}

var ErrNotAllowedToDeletePhoneNumber = &CodedError{
	ErrorValue:      "Phone number can only be removed for an account with a verifed email",
	CodeValue:       "not_allowed_to_delete_phone_number",
	StatusCodeValue: http.StatusForbidden,
}

var ErrInvalidParameterCombination = &CodedError{
	ErrorValue:      "phone number or email update cannot be combined with delete pohone number",
	CodeValue:       "invalid_parameter_combination",
	StatusCodeValue: http.StatusBadRequest,
}

var ErrPhoneNumberAlreadyExists = &CodedError{
	ErrorValue:      "Phone number already exists",
	CodeValue:       "phone_number_already_exists",
	StatusCodeValue: http.StatusUnprocessableEntity,
}

var ErrInvalidURL = &CodedError{
	ErrorValue:      "provided url is invalid",
	CodeValue:       "invalid_url",
	StatusCodeValue: http.StatusBadRequest,
}

var ErrNotAllowedToChangeEmail = &CodedError{
	ErrorValue:      fmt.Sprintf("You are not allowed to change your email more than once every %d hours.", 24*EmailChangeCooldown),
	CodeValue:       "not_allowed_to_change_email",
	StatusCodeValue: http.StatusForbidden,
}

var ErrInvalidToken = &CodedError{
	ErrorValue:      fmt.Sprintf("Invalid or expired Token"),
	CodeValue:       "invalid_token",
	StatusCodeValue: http.StatusForbidden,
}

var ErrEmptyToken = &CodedError{
	ErrorValue:      fmt.Sprintf("Token cannot be empty"),
	CodeValue:       "empty_token",
	StatusCodeValue: http.StatusBadRequest,
}

var ErrBannedWord = &CodedError{
	ErrorValue:      fmt.Sprintf("This user name may be offensive and is not allowed."),
	CodeValue:       "login_use_banned_words",
	StatusCodeValue: http.StatusBadRequest,
}

var ErrRequestsThrottled = &CodedError{
	ErrorValue:      "You are updating too fast. Please try again later",
	CodeValue:       "too_many_requests",
	StatusCodeValue: http.StatusTooManyRequests,
}

var ErrAuthFailure = &CodedError{
	ErrorValue:      "Fail to auth.",
	CodeValue:       "auth_failure",
	StatusCodeValue: http.StatusForbidden,
}

var ErrInvalidGameID = &CodedError{
	ErrorValue:      fmt.Sprintf("Game id is invalid"),
	CodeValue:       "invalid_game_id",
	StatusCodeValue: http.StatusBadRequest,
}

var ErrGameTooLong = &CodedError{
	ErrorValue:      fmt.Sprintf("Game name exceeds %d characters.", GameMaxLen),
	CodeValue:       "game_name_too_long",
	StatusCodeValue: http.StatusBadRequest,
}

var ErrGameProtected = &CodedError{
	ErrorValue:      "Game ID cannot be used.",
	CodeValue:       "game_id_protected",
	StatusCodeValue: http.StatusBadRequest,
}

var ErrInvalidBirthday = &CodedError{
	ErrorValue:      "Age cannot be under 13 years",
	CodeValue:       "invalid_birthday",
	StatusCodeValue: http.StatusForbidden,
}

var ErrHexColorInvalid = &CodedError{
	ErrorValue:      "Hex color is not valid.",
	CodeValue:       "invalid_hex_color",
	StatusCodeValue: http.StatusBadRequest,
}

var ErrLocaleInvalid = &CodedError{
	ErrorValue:      "The specified locale is invalid",
	CodeValue:       "language_not_valid",
	StatusCodeValue: http.StatusBadRequest,
}

var ErrInvalidLastLogin = &CodedError{
	ErrorValue:      "Last Login doesn't follow format \"%Y-%m-%d %H:%M:%S\"",
	CodeValue:       "invalid_last_login",
	StatusCodeValue: http.StatusBadRequest,
}

var ErrStatusTooLong = &CodedError{
	ErrorValue:      "Title is too long.",
	CodeValue:       "status_too_long",
	StatusCodeValue: http.StatusBadRequest,
}

var ErrStatusUsingBannedWords = &CodedError{
	ErrorValue:      "Status contains banned words.",
	CodeValue:       "status_use_banned_words",
	StatusCodeValue: http.StatusBadRequest,
}

var ErrUserNotEligible = &CodedError{
	ErrorValue:      "Action does not apply to this user.",
	CodeValue:       "user_not_eligible",
	StatusCodeValue: http.StatusForbidden,
}

// ErrSudoReauthNeeded is returned when the `login_at` parameter is missing,
// or the value is invalid.
var ErrSudoReauthNeeded = &CodedError{
	ErrorValue:      "The login_auth assertion is invalid",
	CodeValue:       "reauth_needed",
	StatusCodeValue: http.StatusForbidden,
}

var ErrAdminLoginRequired = &CodedError{
	ErrorValue:      "An admin login must be provided for deletes",
	CodeValue:       "admin_login_required",
	StatusCodeValue: http.StatusBadRequest,
}

type ErrBadIdentifier struct {
	Name  string
	Value string
}

func (e *ErrBadIdentifier) Error() string {
	return "Invalid " + e.Name + " parameter requested: " + e.Value
}

func (e *ErrBadIdentifier) Code() string    { return "invalid_parameter" }
func (e *ErrBadIdentifier) ShouldLog() bool { return false }
func (e *ErrBadIdentifier) StatusCode() int { return http.StatusBadRequest }

func init() {
	for _, e := range codeErrs {
		ErrMap[e.Code()] = e
	}
}
