package data

import (
	"fmt"
)

type ErrorCode int

const (
	// 400 variants

	// 401 variants
	errCodeInvalidCredentials ErrorCode = 4011
	errCodeInvalidAuthToken   ErrorCode = 4012
	errCodeAuthRequired       ErrorCode = 4013

	// 403 variants
	errCodeInactiveAccount ErrorCode = 4031
	errCodeNotPermitted    ErrorCode = 4032

	// 409 variants
	errCodeEditConflict       ErrorCode = 4091
	errCodeAlreadyVoted       ErrorCode = 4092
	errCodeAlreadyBlindSigned ErrorCode = 4093
	errCodeVoteAlreadyCast    ErrorCode = 4094

	// 422 variants
	errCodeValidationFailed ErrorCode = 4221
	errCodeBlindedVoteRange ErrorCode = 4222
	errCodeInvalidSignature ErrorCode = 4223
	errCodeHasNotStarted    ErrorCode = 4224
)

type Error struct {
	HttpCode int               `json:"http_code,omitempty"`
	Code     ErrorCode         `json:"code,omitempty"`
	Message  string            `json:"message"`
	Details  map[string]string `json:"details,omitempty"`
}

func (e *Error) Error() string {
	return e.Message
}

func (e *Error) WithDetails(details map[string]string) error {
	e.Details = details
	return e
}


func (e *Error) DetailMessages() []string {
    msgs := make([]string, 0, len(e.Details))
    for key, msg := range e.Details {
        msgs = append(msgs, fmt.Sprintf("%s: %s", key, msg))
    }
    return msgs
}

func New(httpCode int, code ErrorCode, text string) error {
	return &Error{HttpCode: httpCode, Code: code, Message: text}
}

var (
    // 400 Bad Request
    ErrFailedPEM       = New(400, 400, "failed to decode PEM block")
    ErrBadlyFormedJSON = New(400, 400, "body contains badly-formed JSON")
    ErrBodyEmpty       = New(400, 400, "body must not be empty")
    ErrSingleValue     = New(400, 400, "body must only contain a single JSON value")
    ErrInvalidID       = New(400, 400, "invalid id parameter")
	ErrBadRequest      = New(400, 400, "the server cannot process the request due to a client error")

    // 401 Unauthorized
    ErrInvalidCredentials = New(401, errCodeInvalidCredentials, "invalid credentials")
    ErrInvalidAuthToken   = New(401, errCodeInvalidAuthToken, "invalid or missing authentication token")
    ErrNoToken            = New(401, errCodeInvalidAuthToken, "token must be provided")
    ErrAuthRequired       = New(401, errCodeAuthRequired, "you must be authenticated to access this resource")

    // 403 Forbidden
    ErrInactiveAccount = New(403, errCodeInactiveAccount, "your user account must be activated to access this resource")
    ErrNotPermitted    = New(403, errCodeNotPermitted, "your user account doesn't have the necessary permissions to access this resource")

    // 404 Not Found
    ErrRecordNotFound = New(404, 404, "record not found")
    ErrNoPath         = New(404, 404, "path is required")

    // 409 Conflict
    ErrEditConflict       = New(409, errCodeEditConflict, "edit conflict")
    ErrDuplicateVote      = New(409, errCodeAlreadyVoted, "this signature has already been used to cast a vote")
    ErrDuplicateBlindSign = New(409, errCodeAlreadyBlindSigned, "user has already requested a blind signature for this issue")
    ErrDuplicateSignature = New(409, errCodeVoteAlreadyCast, "this signature has already been used to cast a vote")
    ErrDuplicateEmail     = New(409, 409, "duplicate email")
    ErrDuplicateUser      = New(409, 409, "duplicate username")

    // 422 Unprocessable Entity
    ErrValidationFailed   = New(422, errCodeValidationFailed, "validation failed")
    ErrInvalidBlindedVote = New(422, errCodeBlindedVoteRange, "blinded_vote is out of valid range [1, n-1]")
    ErrInvalidSignature   = New(422, errCodeInvalidSignature, "signature verification failed")
	ErrHasNotStarted      = New(422, errCodeHasNotStarted, "the vote has not yet started")

    // 429 Too Many Requests
    ErrRateLimitExceeded = New(429, 429, "rate limit exceeded")
)
