package errors

import (
	"context"
	"errors"
	"fmt"
	"github.com/UpmeshLTD/urus-aio/infrastructure/db/entities"

	"github.com/UpmeshLTD/urus-aio/infrastructure/errors/localized"
	"github.com/UpmeshLTD/urus-aio/infrastructure/errors/messages"
)

type ErrorCode int

type Error struct {
	code    ErrorCode
	message string
	error   error
}

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

func (e *Error) ToError() error {
	return errors.New(e.message)
}

func (e *Error) GetCode() ErrorCode {
	return e.code
}

func (e *Error) GetMessage() string {
	return e.message
}

func (e *Error) GetError(err error) error {
	return e.error
}

func (e *Error) SetError(err error) *Error {
	e.error = err
	return e
}

func (ec ErrorCode) Value() int {
	return int(ec)
}

func NewError(code ErrorCode, message string) *Error {
	return &Error{
		code:    code,
		message: message,
	}
}

func New(ctx context.Context, errorCode ErrorCode, messageCode messages.MessageCode) *Error {
	return &Error{
		code:    errorCode,
		message: NewMessage(ctx, messageCode),
	}
}

func Default(ctx context.Context, messageCode messages.MessageCode) *Error {
	return New(ctx, Other, messageCode)
}

func NewFormatted(ctx context.Context, errorCode ErrorCode, messageCode messages.MessageCode, args ...interface{}) *Error {
	return &Error{
		code:    errorCode,
		message: fmt.Sprintf(NewMessage(ctx, messageCode), args...),
	}
}

func NewMessage(ctx context.Context, messageCode messages.MessageCode) string {
	language := getContextLanguage(ctx)
	if text, ok := languages[language][messageCode]; ok && text != "" {
		return text
	}
	if text, ok := languages[defaultLanguage][messageCode]; ok && text != "" {
		return text
	}
	return fmt.Sprintf("%s (message code: %d)", languages[defaultLanguage][messages.Other], messageCode.Value())
}

func NewMessageFormatted(ctx context.Context, messageCode messages.MessageCode, args ...interface{}) string {
	return fmt.Sprintf(NewMessage(ctx, messageCode), args...)
}

func getContextLanguage(ctx context.Context) string {
	language := ctx.Value("Language")
	if language != nil {
		return language.(string)
	}
	return defaultLanguage
}

var defaultLanguage = entities.LanguageEN

var languages = map[string]map[interface{}]string{
	entities.LanguageEN: localized.English,
	entities.LanguageTH: localized.Thailand,
	entities.LanguageCN: localized.Chinese,
	entities.LanguageMY: localized.Malaysia,
}

const (
	Other                                   ErrorCode = 1
	InternalServer                          ErrorCode = 1000
	CourierNotSupport                       ErrorCode = 1001
	CourierAuthenticationInvalid            ErrorCode = 1010
	RecipientAddressInvalid                 ErrorCode = 1020
	PickupAddressInvalid                    ErrorCode = 1021
	PickupAddressAndRecipientAddressInvalid ErrorCode = 1022
	RecipientPhoneInvalid                   ErrorCode = 1023
	DimensionInvalid                        ErrorCode = 1030
	DeliveryPlanInvalid                     ErrorCode = 1031
	OrderExists                             ErrorCode = 1040
	OtherShipmentError                      ErrorCode = 1050
)
