package apperrorsx

import (
	"strconv"
	"strings"

	"github.com/gofiber/fiber/v2"

	"wucher/internal/transport/http/jsonapi"
)

var (
	registryByCode      map[string]Def
	registryByErrorCode map[string]Def
)

func init() {
	registryByCode = make(map[string]Def, len(Registry))
	registryByErrorCode = make(map[string]Def, len(Registry))
	for _, def := range Registry {
		registryByCode[def.Code] = def
		registryByErrorCode[def.ErrorCode] = def
	}
}

func EnrichErrorObjects(status int, errs []jsonapi.ErrorObject) []jsonapi.ErrorObject {
	out := make([]jsonapi.ErrorObject, 0, len(errs))
	for i := range errs {
		obj := errs[i]
		obj.Status = strconv.Itoa(status)

		def := inferDef(status, obj)
		if obj.Code == "" && def.Code != "" {
			obj.Code = def.Code
		}
		if obj.ErrorCode == "" && def.ErrorCode != "" {
			obj.ErrorCode = def.ErrorCode
		}
		if obj.Title == "" && def.Title != "" {
			obj.Title = def.Title
		}
		out = append(out, obj)
	}
	return out
}

func inferDef(status int, obj jsonapi.ErrorObject) Def {
	if obj.Code != "" {
		if def, ok := registryByCode[obj.Code]; ok {
			def.Status = status
			def.ErrorCode = Build(status, moduleFromErrorCode(def.ErrorCode), caseFromErrorCode(def.ErrorCode), def.Code, def.Message).ErrorCode
			return def
		}
	}
	if obj.ErrorCode != "" {
		if def, ok := registryByErrorCode[obj.ErrorCode]; ok {
			return def
		}
	}

	switch status {
	case fiber.StatusBadRequest:
		return ErrInvalidRequest
	case fiber.StatusUnauthorized:
		return ErrUserUnauthorized
	case fiber.StatusForbidden:
		return ErrForbiddenAccess
	case fiber.StatusNotFound:
		return ErrDataNotFound
	case fiber.StatusConflict:
		return ErrBusinessRuleFailed
	case fiber.StatusUnprocessableEntity:
		return ErrValidation
	case fiber.StatusTooManyRequests:
		return ErrPINAttemptLimitReached
	default:
		msg := strings.ToLower(strings.TrimSpace(obj.Detail))
		if strings.Contains(msg, "not found") {
			return ErrDataNotFound
		}
		if strings.Contains(msg, "unauthorized") {
			return ErrUserUnauthorized
		}
		if strings.Contains(msg, "forbidden") {
			return ErrForbiddenAccess
		}
		if strings.Contains(msg, "validation") || strings.Contains(msg, "invalid") {
			return ErrValidation
		}
		return ErrInternal
	}
}

func moduleFromErrorCode(errorCode string) string {
	if len(errorCode) >= 5 {
		return errorCode[3:5]
	}
	return ModuleGeneral
}

func caseFromErrorCode(errorCode string) string {
	if len(errorCode) >= 7 {
		return errorCode[5:7]
	}
	return CaseGeneralInternalError
}
