package service

import (
	"fmt"
	"net/http"
)

var (
	ErrInvalidToken               = new(20000, http.StatusUnauthorized, "invalid or expired token")
	ErrCreatePersonalTokenLimited = new(20001, http.StatusBadRequest, "cannot create more token")
	ErrMaxOffset                  = func(expected, actual int) *Error {
		return new(20101, http.StatusBadRequest, "offset %d is too large, max offset is %d", actual, expected)
	}

	ErrCurrentUserNotFound = new(20100, http.StatusUnauthorized, "current user not found")
)

type Error struct {
	// Code is the error code.
	Code    int    `json:"code"`
	Message string `json:"message"`

	// HTTPStatus is the HTTP Status Code that should return to client.
	HTTPStatus int `json:"-"`
}

func new(code int, httpStatus int, msg string, args ...interface{}) *Error {
	return &Error{
		Code:       code,
		HTTPStatus: httpStatus,
		Message:    fmt.Sprintf(msg, args...),
	}
}

func New(httpStatus int, err ...error) *Error {
	msg := http.StatusText(httpStatus)
	if len(err) > 0 {
		e := err[0]
		if se, ok := e.(*Error); ok {
			msg = se.Message
		} else {
			msg = fmt.Sprint(e)
		}
	}
	return &Error{
		Code:       httpStatus,
		HTTPStatus: httpStatus,
		Message:    msg,
	}
}

func NewMsg(httpStatus int, msg string, args ...interface{}) *Error {
	return New(httpStatus).WithMsg(fmt.Sprintf(msg, args...))
}

func (e *Error) WithMsg(msg string) *Error {
	return &Error{
		Code:       e.Code,
		HTTPStatus: e.HTTPStatus,
		Message:    fmt.Sprintf("%s: %s", e.Message, msg),
	}
}

func (lhs *Error) Is(rhs error) bool {
	e, ok := rhs.(*Error)
	return ok && lhs.Code == e.Code
}

func (e *Error) Error() string {
	return fmt.Sprintf("Error %d: %v", e.Code, e.Message)
}
