package appcontext

import (
	"context"
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
)

type key string

const (
	CorrelationIDKey  = key("CorrelationID")
	OrganizationIDKey = key("OrganizationIDKey")
	UserIDKey         = key("UserID")
	OutletIDKey       = key("OutletID")
	RoleIDKey         = key("RoleID")
	AppVersionKey     = key("AppVersion")
	AppIDKey          = key("AppID")
	AppTypeKey        = key("AppType")
	PlatformKey       = key("platform")
	DeviceOSKey       = key("deviceOS")
	UserLocaleKey     = key("userLocale")
	UserRoleKey       = key("userRole")
)

func LogFields(ctx interface{}) map[string]interface{} {
	fields := make(map[string]interface{})
	fields[string(CorrelationIDKey)] = value(ctx, CorrelationIDKey)
	fields[string(OrganizationIDKey)] = value(ctx, OrganizationIDKey)
	fields[string(OutletIDKey)] = value(ctx, OutletIDKey)
	fields[string(AppVersionKey)] = value(ctx, AppVersionKey)
	fields[string(AppIDKey)] = value(ctx, AppIDKey)
	fields[string(AppTypeKey)] = value(ctx, AppTypeKey)
	fields[string(UserIDKey)] = value(ctx, UserIDKey)
	fields[string(PlatformKey)] = value(ctx, PlatformKey)
	fields[string(DeviceOSKey)] = value(ctx, DeviceOSKey)
	fields[string(UserLocaleKey)] = value(ctx, UserLocaleKey)
	return fields
}

func value(ctx interface{}, key key) string {
	switch c := ctx.(type) {
	case *gin.Context:
		return getFromGinContext(c, key)
	case context.Context:
		return getFromGoContext(c, key)
	default:
		return ""
	}
}

func uuidValue(ctx interface{}, key key) uuid.UUID {
	switch c := ctx.(type) {
	case *gin.Context:
		val, _ := uuid.Parse(getFromGinContext(c, key))
		return val
	case context.Context:
		val, _ := uuid.Parse(getFromGoContext(c, key))
		return val
	default:
		return uuid.New()
	}
}

func getFromGinContext(c *gin.Context, key key) string {
	keyStr := string(key)
	if val, exists := c.Get(keyStr); exists {
		if str, ok := val.(string); ok {
			return str
		}
	}
	return getFromGoContext(c.Request.Context(), key)
}

func getFromGoContext(ctx context.Context, key key) string {
	if val, ok := ctx.Value(key).(string); ok {
		return val
	}
	return ""
}
