package economy

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

	"hakemsho/internal/auth"
	"hakemsho/internal/httpx"
)

// Handler endpointهای فروشگاه و کیف‌پول (همه پشت JWT).
type Handler struct {
	svc *Service
}

func NewHandler(svc *Service) *Handler { return &Handler{svc: svc} }

func uid(r *http.Request) (int64, bool) { return auth.UserID(r.Context()) }

// Wallet — GET /api/wallet
func (h *Handler) Wallet(w http.ResponseWriter, r *http.Request) {
	id, ok := uid(r)
	if !ok {
		httpx.Error(w, http.StatusUnauthorized, "unauthorized")
		return
	}
	p, err := h.svc.GetProfile(r.Context(), id)
	if err != nil {
		httpx.Error(w, http.StatusNotFound, "user not found")
		return
	}
	httpx.JSON(w, http.StatusOK, p)
}

// Shop — GET /api/shop
func (h *Handler) Shop(w http.ResponseWriter, r *http.Request) {
	id, ok := uid(r)
	if !ok {
		httpx.Error(w, http.StatusUnauthorized, "unauthorized")
		return
	}
	owned, err := h.svc.OwnedCards(r.Context(), id)
	if err != nil {
		httpx.Error(w, http.StatusInternalServerError, "server error")
		return
	}
	p, _ := h.svc.GetProfile(r.Context(), id)
	selected := "simple"
	if p != nil {
		selected = p.SelectedCard
	}
	httpx.JSON(w, http.StatusOK, map[string]any{
		"catalog":       h.svc.Catalog(),
		"owned_cards":   owned,
		"selected_card": selected,
	})
}

type cardReq struct {
	CardID string `json:"card_id"`
}

// BuyCard — POST /api/shop/buy-card
func (h *Handler) BuyCard(w http.ResponseWriter, r *http.Request) {
	id, ok := uid(r)
	if !ok {
		httpx.Error(w, http.StatusUnauthorized, "unauthorized")
		return
	}
	var req cardReq
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		httpx.Error(w, http.StatusBadRequest, "invalid body")
		return
	}
	switch err := h.svc.BuyCard(r.Context(), id, req.CardID); {
	case err == nil:
		httpx.JSON(w, http.StatusOK, map[string]string{"message": "ok"})
	case errors.Is(err, ErrNotFound):
		httpx.Error(w, http.StatusNotFound, "card not found")
	case errors.Is(err, ErrAlreadyOwned):
		httpx.Error(w, http.StatusConflict, "already owned")
	case errors.Is(err, ErrInsufficient):
		httpx.Error(w, http.StatusPaymentRequired, "insufficient coins")
	default:
		httpx.Error(w, http.StatusInternalServerError, "server error")
	}
}

// Carpets — GET /api/carpets (فرش‌ها + مالکیت + فرشِ انتخابی)
func (h *Handler) Carpets(w http.ResponseWriter, r *http.Request) {
	id, ok := uid(r)
	if !ok {
		httpx.Error(w, http.StatusUnauthorized, "unauthorized")
		return
	}
	carpets, err := h.svc.Carpets(r.Context(), id)
	if err != nil {
		httpx.Error(w, http.StatusInternalServerError, "server error")
		return
	}
	selected := "classic"
	if p, _ := h.svc.GetProfile(r.Context(), id); p != nil && p.SelectedCarpet != "" {
		selected = p.SelectedCarpet
	}
	httpx.JSON(w, http.StatusOK, map[string]any{
		"carpets":  carpets,
		"selected": selected,
	})
}

// BuyCarpet — POST /api/shop/buy-carpet
func (h *Handler) BuyCarpet(w http.ResponseWriter, r *http.Request) {
	h.carpetAction(w, r, false)
}

// SelectCarpet — POST /api/shop/select-carpet
func (h *Handler) SelectCarpet(w http.ResponseWriter, r *http.Request) {
	h.carpetAction(w, r, true)
}

func (h *Handler) carpetAction(w http.ResponseWriter, r *http.Request, sel bool) {
	id, ok := uid(r)
	if !ok {
		httpx.Error(w, http.StatusUnauthorized, "unauthorized")
		return
	}
	var req struct {
		CarpetID string `json:"carpet_id"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		httpx.Error(w, http.StatusBadRequest, "invalid body")
		return
	}
	var err error
	if sel {
		err = h.svc.SelectCarpet(r.Context(), id, req.CarpetID)
	} else {
		err = h.svc.BuyCarpet(r.Context(), id, req.CarpetID)
	}
	switch {
	case err == nil:
		httpx.JSON(w, http.StatusOK, map[string]string{"message": "ok"})
	case errors.Is(err, ErrNotFound):
		httpx.Error(w, http.StatusNotFound, "carpet not found")
	case errors.Is(err, ErrAlreadyOwned):
		httpx.Error(w, http.StatusConflict, "already owned")
	case errors.Is(err, ErrNotOwned):
		httpx.Error(w, http.StatusForbidden, "carpet not owned")
	case errors.Is(err, ErrInsufficient):
		httpx.Error(w, http.StatusPaymentRequired, "insufficient coins")
	default:
		httpx.Error(w, http.StatusInternalServerError, "server error")
	}
}

// ChatPacks — GET /api/chat-packs (بسته‌های پیام/شکلک + مالکیتِ کاربر)
func (h *Handler) ChatPacks(w http.ResponseWriter, r *http.Request) {
	id, ok := uid(r)
	if !ok {
		httpx.Error(w, http.StatusUnauthorized, "unauthorized")
		return
	}
	packs, err := h.svc.ChatPacks(r.Context(), id)
	if err != nil {
		httpx.Error(w, http.StatusInternalServerError, "server error")
		return
	}
	httpx.JSON(w, http.StatusOK, map[string]any{"packs": packs})
}

// BuyChatPack — POST /api/shop/buy-chat-pack
func (h *Handler) BuyChatPack(w http.ResponseWriter, r *http.Request) {
	id, ok := uid(r)
	if !ok {
		httpx.Error(w, http.StatusUnauthorized, "unauthorized")
		return
	}
	var req struct {
		PackID string `json:"pack_id"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		httpx.Error(w, http.StatusBadRequest, "invalid body")
		return
	}
	switch err := h.svc.BuyChatPack(r.Context(), id, req.PackID); {
	case err == nil:
		httpx.JSON(w, http.StatusOK, map[string]string{"message": "ok"})
	case errors.Is(err, ErrNotFound):
		httpx.Error(w, http.StatusNotFound, "pack not found")
	case errors.Is(err, ErrAlreadyOwned):
		httpx.Error(w, http.StatusConflict, "already owned")
	case errors.Is(err, ErrInsufficient):
		httpx.Error(w, http.StatusPaymentRequired, "insufficient coins")
	default:
		httpx.Error(w, http.StatusInternalServerError, "server error")
	}
}

// Tournaments — GET /api/tournaments (فهرست با وضعیتِ عضویتِ کاربر)
func (h *Handler) Tournaments(w http.ResponseWriter, r *http.Request) {
	id, ok := uid(r)
	if !ok {
		httpx.Error(w, http.StatusUnauthorized, "unauthorized")
		return
	}
	list, err := h.svc.ListTournaments(r.Context(), id)
	if err != nil {
		httpx.Error(w, http.StatusInternalServerError, "server error")
		return
	}
	httpx.JSON(w, http.StatusOK, map[string]any{"tournaments": list})
}

// TournamentStandings — GET /api/tournaments/standings?id=X (رده‌بندی)
func (h *Handler) TournamentStandings(w http.ResponseWriter, r *http.Request) {
	if _, ok := uid(r); !ok {
		httpx.Error(w, http.StatusUnauthorized, "unauthorized")
		return
	}
	tid, _ := strconv.ParseInt(r.URL.Query().Get("id"), 10, 64)
	if tid == 0 {
		httpx.Error(w, http.StatusBadRequest, "invalid id")
		return
	}
	rows, err := h.svc.TournamentStandings(r.Context(), tid, 50)
	if err != nil {
		httpx.Error(w, http.StatusInternalServerError, "server error")
		return
	}
	httpx.JSON(w, http.StatusOK, map[string]any{"standings": rows})
}

// JoinTournament — POST /api/tournaments/join {id}
func (h *Handler) JoinTournament(w http.ResponseWriter, r *http.Request) {
	id, ok := uid(r)
	if !ok {
		httpx.Error(w, http.StatusUnauthorized, "unauthorized")
		return
	}
	var req struct {
		ID int64 `json:"id"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		httpx.Error(w, http.StatusBadRequest, "invalid body")
		return
	}
	switch err := h.svc.JoinTournament(r.Context(), id, req.ID); {
	case err == nil:
		httpx.JSON(w, http.StatusOK, map[string]string{"message": "ثبت‌نام انجام شد"})
	case errors.Is(err, ErrTournamentNotFound):
		httpx.Error(w, http.StatusNotFound, "تورنومنت یافت نشد")
	case errors.Is(err, ErrTournamentClosed):
		httpx.Error(w, http.StatusConflict, "مهلتِ ثبت‌نام تمام شده")
	case errors.Is(err, ErrAlreadyJoined):
		httpx.Error(w, http.StatusConflict, "قبلاً ثبت‌نام کرده‌اید")
	case errors.Is(err, ErrInsufficient):
		httpx.Error(w, http.StatusPaymentRequired, "insufficient coins")
	default:
		httpx.Error(w, http.StatusInternalServerError, "server error")
	}
}

// SelectCard — POST /api/shop/select-card
func (h *Handler) SelectCard(w http.ResponseWriter, r *http.Request) {
	id, ok := uid(r)
	if !ok {
		httpx.Error(w, http.StatusUnauthorized, "unauthorized")
		return
	}
	var req cardReq
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		httpx.Error(w, http.StatusBadRequest, "invalid body")
		return
	}
	switch err := h.svc.SelectCard(r.Context(), id, req.CardID); {
	case err == nil:
		httpx.JSON(w, http.StatusOK, map[string]string{"message": "ok"})
	case errors.Is(err, ErrNotFound):
		httpx.Error(w, http.StatusNotFound, "card not found")
	case errors.Is(err, ErrNotOwned):
		httpx.Error(w, http.StatusForbidden, "card not owned")
	default:
		httpx.Error(w, http.StatusInternalServerError, "server error")
	}
}

// Purchase — POST /api/shop/purchase (تأیید خرید IAP)
func (h *Handler) Purchase(w http.ResponseWriter, r *http.Request) {
	id, ok := uid(r)
	if !ok {
		httpx.Error(w, http.StatusUnauthorized, "unauthorized")
		return
	}
	var req PurchaseRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		httpx.Error(w, http.StatusBadRequest, "invalid body")
		return
	}
	switch err := h.svc.VerifyPurchase(r.Context(), id, req); {
	case err == nil:
		p, _ := h.svc.GetProfile(r.Context(), id)
		httpx.JSON(w, http.StatusOK, map[string]any{"message": "ok", "wallet": p})
	case errors.Is(err, ErrAlreadyOwned):
		httpx.Error(w, http.StatusConflict, "already processed")
	case errors.Is(err, ErrIAPInvalid):
		httpx.Error(w, http.StatusBadRequest, "purchase not valid")
	case errors.Is(err, ErrNotFound):
		httpx.Error(w, http.StatusNotFound, "package not found")
	default:
		httpx.Error(w, http.StatusInternalServerError, "server error")
	}
}

// Stats — GET /api/stats (آمار پروفایل؛ نمایشِ کامل ویژه‌ی کاربران VIP)
func (h *Handler) Stats(w http.ResponseWriter, r *http.Request) {
	id, ok := uid(r)
	if !ok {
		httpx.Error(w, http.StatusUnauthorized, "unauthorized")
		return
	}
	p, err := h.svc.GetProfile(r.Context(), id)
	if err != nil {
		httpx.Error(w, http.StatusNotFound, "user not found")
		return
	}
	resp := map[string]any{"vip": p.VIP}
	if p.VIP {
		st, err := h.svc.GetStats(r.Context(), id)
		if err != nil {
			httpx.Error(w, http.StatusInternalServerError, "server error")
			return
		}
		resp["stats"] = st
	}
	httpx.JSON(w, http.StatusOK, resp)
}

// TablesInfo — GET /api/tables/info (باقیمانده‌ی میزهای خصوصیِ رایگان)
func (h *Handler) TablesInfo(w http.ResponseWriter, r *http.Request) {
	id, ok := uid(r)
	if !ok {
		httpx.Error(w, http.StatusUnauthorized, "unauthorized")
		return
	}
	remaining, unlimited := h.svc.PrivateTableInfo(r.Context(), id)
	httpx.JSON(w, http.StatusOK, map[string]any{
		"remaining": remaining,
		"unlimited": unlimited,
	})
}

// Leaderboard — GET /api/leaderboard (جدولِ رتبه‌بندیِ فصل)
func (h *Handler) Leaderboard(w http.ResponseWriter, r *http.Request) {
	if _, ok := uid(r); !ok {
		httpx.Error(w, http.StatusUnauthorized, "unauthorized")
		return
	}
	list, err := h.svc.Leaderboard(r.Context(), 50)
	if err != nil {
		httpx.Error(w, http.StatusInternalServerError, "server error")
		return
	}
	httpx.JSON(w, http.StatusOK, map[string]any{
		"season":  h.svc.Season(r.Context()),
		"entries": list,
	})
}

// Daily — POST /api/rewards/daily
func (h *Handler) Daily(w http.ResponseWriter, r *http.Request) {
	id, ok := uid(r)
	if !ok {
		httpx.Error(w, http.StatusUnauthorized, "unauthorized")
		return
	}
	switch amount, err := h.svc.ClaimDaily(r.Context(), id); {
	case err == nil:
		httpx.JSON(w, http.StatusOK, map[string]any{"message": "ok", "amount": amount})
	case errors.Is(err, ErrTooSoon):
		httpx.Error(w, http.StatusTooManyRequests, "already claimed today")
	default:
		httpx.Error(w, http.StatusInternalServerError, "server error")
	}
}

type adReq struct {
	Token string `json:"token"`
}

// AdReward — POST /api/rewards/ad (سکه رایگان پس از دیدن کامل تبلیغ)
func (h *Handler) AdReward(w http.ResponseWriter, r *http.Request) {
	id, ok := uid(r)
	if !ok {
		httpx.Error(w, http.StatusUnauthorized, "unauthorized")
		return
	}
	var req adReq
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		httpx.Error(w, http.StatusBadRequest, "invalid body")
		return
	}
	switch amount, err := h.svc.ClaimAdReward(r.Context(), id, req.Token); {
	case err == nil:
		httpx.JSON(w, http.StatusOK, map[string]any{"message": "ok", "amount": amount})
	case errors.Is(err, ErrAdNotVerified):
		httpx.Error(w, http.StatusBadRequest, "ad not verified")
	case errors.Is(err, ErrAdDuplicate):
		httpx.Error(w, http.StatusConflict, "already claimed")
	case errors.Is(err, ErrAdCap):
		httpx.Error(w, http.StatusTooManyRequests, "daily ad cap reached")
	default:
		httpx.Error(w, http.StatusInternalServerError, "server error")
	}
}
