package data

import (
	"fmt"
)

type ErrorCode int

const (
	// 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, 0, "failed to decode PEM block")
    ErrBadlyFormedJSON = New(400, 0, "body contains badly-formed JSON")
    ErrBodyEmpty       = New(400, 0, "body must not be empty")
    ErrSingleValue     = New(400, 0, "body must only contain a single JSON value")
    ErrInvalidID       = New(400, 0, "invalid id parameter")
	ErrBadRequest      = New(400, 0, "the server cannot process the request due to a client error")

    // 401 Unauthorized
    ErrInvalidCredentials = New(401, 4011, "invalid credentials")
    ErrInvalidAuthToken   = New(401, 4012, "invalid or missing authentication token")
    ErrNoToken            = New(401, 4012, "token must be provided")
    ErrAuthRequired       = New(401, 4013, "you must be authenticated to access this resource")
    ErrHasNotStarted      = New(401, 4224, "the vote has not yet started")

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

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

    // 409 Conflict
    ErrEditConflict       = New(409, 4091, "edit conflict")
    ErrDuplicateVote      = New(409, 4092, "this signature has already been used to cast a vote")
    ErrDuplicateBlindSign = New(409, 4093, "user has already requested a blind signature for this issue")
    ErrDuplicateSignature = New(409, 4094, "this signature has already been used to cast a vote")
    ErrDuplicateEmail     = New(409, 0, "duplicate email")
    ErrDuplicateUser      = New(409, 0, "duplicate username")

    // 422 Unprocessable Entity
    ErrValidationFailed   = New(422, 4221, "validation failed")
    ErrInvalidBlindedVote = New(422, 4222, "blinded_vote is out of valid range [1, n-1]")
    ErrInvalidSignature   = New(422, 4223, "signature verification failed")

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