package server

import (
	"context"
	"net/http"

	"galaxy/backend/internal/geo"
	"galaxy/backend/internal/server/handlers"
	"galaxy/backend/internal/server/httperr"
	"galaxy/backend/internal/telemetry"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"go.uber.org/zap"
)

// AdminGeoLister is the narrow contract the admin geo handler needs
// from the geo domain. `*geo.Service` satisfies it directly; tests
// pass a recording fake.
type AdminGeoLister interface {
	ListUserCounters(ctx context.Context, userID uuid.UUID) ([]geo.CountryCounter, error)
}

// AdminGeoHandlers groups the admin-side geo-counter handlers under
// `/api/v1/admin/geo/*`.
type AdminGeoHandlers struct {
	svc    AdminGeoLister
	logger *zap.Logger
}

// NewAdminGeoHandlers constructs the handler set. svc may be nil — in
// that case every handler returns 501 not_implemented, matching the
// pre-Stage-5.8 placeholder behaviour. logger may also be nil; zap.NewNop
// is used in that case.
func NewAdminGeoHandlers(svc AdminGeoLister, logger *zap.Logger) *AdminGeoHandlers {
	if logger == nil {
		logger = zap.NewNop()
	}
	return &AdminGeoHandlers{svc: svc, logger: logger.Named("http.admin.geo")}
}

// adminGeoCountryWire mirrors `GeoCountryCounter` from `openapi.yaml`.
type adminGeoCountryWire struct {
	Country    string  `json:"country"`
	Count      int64   `json:"count"`
	LastSeenAt *string `json:"last_seen_at,omitempty"`
}

// adminGeoListWire mirrors `GeoCountryCounterList` from `openapi.yaml`.
type adminGeoListWire struct {
	UserID string                `json:"user_id"`
	Items  []adminGeoCountryWire `json:"items"`
}

// ListUserCountries handles GET /api/v1/admin/geo/users/{user_id}/countries.
func (h *AdminGeoHandlers) ListUserCountries() gin.HandlerFunc {
	if h.svc == nil {
		return handlers.NotImplemented("adminGeoListUserCountries")
	}
	return func(c *gin.Context) {
		userID, ok := parseUserIDParam(c)
		if !ok {
			return
		}
		ctx := c.Request.Context()
		entries, err := h.svc.ListUserCounters(ctx, userID)
		if err != nil {
			h.logger.Error("admin geo list user countries failed",
				append(telemetry.TraceFieldsFromContext(ctx),
					zap.String("user_id", userID.String()),
					zap.Error(err),
				)...,
			)
			httperr.Abort(c, http.StatusInternalServerError, httperr.CodeInternalError, "service error")
			return
		}
		c.JSON(http.StatusOK, geoCountersToWire(userID, entries))
	}
}

func geoCountersToWire(userID uuid.UUID, entries []geo.CountryCounter) adminGeoListWire {
	out := adminGeoListWire{
		UserID: userID.String(),
		Items:  make([]adminGeoCountryWire, 0, len(entries)),
	}
	for _, e := range entries {
		item := adminGeoCountryWire{
			Country: e.Country,
			Count:   e.Count,
		}
		if e.LastSeenAt != nil {
			formatted := e.LastSeenAt.UTC().Format("2006-01-02T15:04:05.000Z07:00")
			item.LastSeenAt = &formatted
		}
		out.Items = append(out.Items, item)
	}
	return out
}
