package codes

import (
	"encoding/json"
	"net/http"

	"github.com/wangjia/pangolin/server/internal/apierr"
)

// RedeemHandler handles POST /v1/redeem requests.
// It expects the request context to carry the authenticated userID under the
// key ctxKeyUserID (set by the JWT auth middleware from the auth module).
type RedeemHandler struct {
	svc *Service
}

// NewRedeemHandler creates a RedeemHandler backed by svc.
func NewRedeemHandler(svc *Service) *RedeemHandler {
	return &RedeemHandler{svc: svc}
}

// ctxKeyUserID is the context key for the authenticated user ID.
// Defined here to avoid an import cycle; the auth middleware uses the same key.
type ctxKey string

const CtxKeyUserID ctxKey = "user_id"

// redeemRequest is the JSON body for POST /v1/redeem.
type redeemRequest struct {
	Code string `json:"code"`
}

// redeemResponse is the JSON body on success.
type redeemResponse struct {
	Idempotent   bool   `json:"idempotent"`
	Plan         string `json:"plan"`
	DurationDays int    `json:"duration_days"`
	ExpiresAt    string `json:"expires_at,omitempty"` // RFC 3339 UTC
}

// ServeHTTP implements http.Handler.
func (h *RedeemHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
		return
	}

	// Extract authenticated userID from context (set by JWT middleware).
	userID, ok := r.Context().Value(CtxKeyUserID).(int64)
	if !ok || userID == 0 {
		apierr.WriteJSON(w, http.StatusUnauthorized, &apierr.Error{
			Code:      "UNAUTHORIZED",
			MessageZH: "请先登录",
			MessageEn: "Authentication required",
		})
		return
	}

	var req redeemRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil || req.Code == "" {
		apierr.WriteJSON(w, http.StatusBadRequest, apierr.ErrBadRequest)
		return
	}

	result, apiErr := h.svc.Redeem(r.Context(), RedeemRequest{
		UserID: userID,
		Code:   req.Code,
	})
	if apiErr != nil {
		status := http.StatusBadRequest
		switch apiErr.Code {
		case "ACCOUNT_LOCKED", "RATE_LIMITED":
			status = http.StatusTooManyRequests
		case "INTERNAL_ERROR":
			status = http.StatusInternalServerError
		case "CODE_REDEEMED", "CODE_NOT_FOUND", "CODE_VOID", "INVALID_CODE":
			status = http.StatusBadRequest
		}
		apierr.WriteJSON(w, status, apiErr)
		return
	}

	resp := redeemResponse{
		Idempotent:   result.Idempotent,
		Plan:         string(result.PlanCode),
		DurationDays: result.DurationDays,
	}
	if !result.ExpiresAt.IsZero() {
		resp.ExpiresAt = result.ExpiresAt.UTC().Format("2006-01-02T15:04:05Z")
	}

	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.WriteHeader(http.StatusOK)
	_ = json.NewEncoder(w).Encode(resp)
}
