package api

import (
	"fmt"
	"time"

	"github.com/kataras/iris/v12"
)

// Error holds the error sent by server to clients (JSON).
type Error struct {
	StatusCode int    `json:"code"`
	Method     string `json:"-"`
	Path       string `json:"-"`
	Message    string `json:"message"`
	Timestamp  int64  `json:"timestamp"`
}

func newError(statusCode int, method, path, format string, args ...interface{}) Error {
	msg := format
	if len(args) > 0 {
		// why we check for that? If the original error message came from our database
		// and it contains fmt-reserved words
		// like %s or %d we will get MISSING(=...) in our error message and we don't want that.
		msg = fmt.Sprintf(msg, args...)
	}

	if msg == "" {
		msg = iris.StatusText(statusCode)
	}

	return Error{
		StatusCode: statusCode,
		Method:     method,
		Path:       path,
		Message:    msg,
		Timestamp:  time.Now().Unix(),
	}
}

// Error implements the internal Go error interface.
func (e Error) Error() string {
	return fmt.Sprintf("[%d] %s: %s: %s", e.StatusCode, e.Method, e.Path, e.Message)
}

// Is implements the standard `errors.Is` internal interface.
// Usage: errors.Is(e, target)
func (e Error) Is(target error) bool {
	if target == nil {
		return false
	}

	err, ok := target.(Error)
	if !ok {
		return false
	}

	return (err.StatusCode == e.StatusCode || e.StatusCode == 0) &&
		(err.Message == e.Message || e.Message == "")
}