package handlers

import (
	"context"
	"strings"
	"time"

	"github.com/gofiber/fiber/v2"

	"wucher/internal/shared/pkg/userctx"
	"wucher/internal/shared/pkg/uuidv7"
	responsedto "wucher/internal/transport/http/dto/response"
)

func userRef(id []byte, name string) *responsedto.UserRef {
	trimmed := strings.TrimSpace(name)
	if len(id) == 0 && trimmed == "" {
		return nil
	}
	if trimmed == "" && len(id) > 0 {
		trimmed = userctx.GetDisplayName(context.TODO(), id)
	}
	return &responsedto.UserRef{
		UUID: idString(id),
		Name: trimmed,
	}
}

// updatedAuditOrCreated returns the "last modified" timestamp (RFC3339) and actor for an
// audited row, falling back to the creation timestamp/actor when the row was never
// updated after creation (updated_by unset — e.g. seed/legacy rows). This guarantees an
// updated_at/updated_by is always presentable instead of silently omitted.
func updatedAuditOrCreated(updatedAt, createdAt time.Time, updatedBy []byte, updatedByName string, createdBy []byte, createdByName string) (string, *responsedto.UserRef) {
	if len(updatedBy) == 0 {
		updatedBy, updatedByName = createdBy, createdByName
	}
	if updatedAt.IsZero() {
		updatedAt = createdAt
	}
	at := ""
	if !updatedAt.IsZero() {
		at = updatedAt.UTC().Format(time.RFC3339)
	}
	return at, userRef(updatedBy, updatedByName)
}

func actorUserID(c *fiber.Ctx) []byte {
	raw := c.Locals("user_id")
	userID, ok := raw.([]byte)
	if !ok || len(userID) == 0 {
		return nil
	}
	// Return a copy to avoid accidental mutation of local context values.
	return append([]byte(nil), userID...)
}

// uuidStringOrEmpty resolves a user ID to their last name using the global userctx getter
// if set; otherwise falls back to the UUID string representation.
func uuidStringOrEmpty(id []byte) string {
	if len(id) == 0 {
		return ""
	}
	if name := userctx.GetDisplayName(context.TODO(), id); name != "" {
		return name
	}
	// Fallback to UUID string when no getter is available or user not found.
	val, err := uuidv7.BytesToString(id)
	if err != nil {
		return ""
	}
	return val
}

// idString converts a resource ID byte slice to a UUID string.
// Unlike uuidStringOrEmpty, this does not attempt user name resolution.
// Use this for primary keys and resource IDs, not for audit/user reference fields.
func idString(id []byte) string {
	if len(id) == 0 {
		return ""
	}
	val, err := uuidv7.BytesToString(id)
	if err != nil {
		return ""
	}
	return val
}

func timeStringOrEmpty(t *time.Time) string {
	if t == nil || t.IsZero() {
		return ""
	}
	return t.UTC().Format(time.RFC3339)
}
