package admin

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/go-chi/chi/v5"

	"hakemsho/internal/economy"
	"hakemsho/internal/httpx"
)

// APIRoutes زیرروترِ JSON پنلِ ادمینِ Next.js را برمی‌گرداند (پشتِ همان Basic Auth).
// سمتِ کلاینت، هدرِ Authorization: Basic را ذخیره و در هر درخواست می‌فرستد.
func (h *Handler) APIRoutes() http.Handler {
	r := chi.NewRouter()
	r.Get("/me", h.apiMe)
	r.Get("/dashboard", h.apiDashboard)

	r.Get("/catalog", h.apiCatalog)
	r.Post("/catalog/{kind}", h.apiCatalogUpsert)
	r.Delete("/catalog/{kind}", h.apiCatalogDelete)
	r.Post("/catalog/card/{id}/upload", h.uploadDeck) // multipart (اشتراکی با HTML)

	r.Get("/users", h.apiUsers)
	r.Post("/users/coins", h.apiUserCoins)
	r.Post("/users/grant", h.apiUserGrant)

	r.Get("/carpets", h.apiCarpets)
	r.Post("/carpets", h.apiCarpetUpsert)
	r.Delete("/carpets", h.apiCarpetDelete)
	r.Post("/carpets/{id}/upload", h.carpetUpload) // multipart (اشتراکی)

	r.Get("/chat", h.apiChat)
	r.Post("/chat/pack", h.apiChatPackUpsert)
	r.Delete("/chat/pack", h.apiChatPackDelete)
	r.Post("/chat/message", h.apiChatMessageAdd)
	r.Delete("/chat/message", h.apiChatMessageDelete)

	r.Get("/tournaments", h.apiTournaments)
	r.Post("/tournaments", h.apiTournamentAdd)
	r.Put("/tournaments", h.apiTournamentUpdate)
	r.Delete("/tournaments", h.apiTournamentDelete)

	r.Post("/season/reset", h.apiSeasonReset)
	return r
}

func (h *Handler) apiMe(w http.ResponseWriter, _ *http.Request) {
	httpx.JSON(w, http.StatusOK, map[string]any{"ok": true})
}

func (h *Handler) apiDashboard(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	count := func(q string) int64 {
		var n int64
		_ = h.db.QueryRowContext(ctx, q).Scan(&n)
		return n
	}
	httpx.JSON(w, http.StatusOK, map[string]any{
		"users":       count(`SELECT COUNT(*) FROM users`),
		"purchases":   count(`SELECT COUNT(*) FROM purchases WHERE status='verified'`),
		"games":       count(`SELECT COUNT(*) FROM game_history`),
		"coins":       count(`SELECT COALESCE(SUM(coins),0) FROM users`),
		"tournaments": count(`SELECT COUNT(*) FROM tournaments`),
		"season":      h.eco.Season(ctx),
	})
}

// --- کاتالوگِ فروشگاه ---

var catalogTable = map[string]string{
	"coin": "coin_packages", "ticket": "ticket_packages", "card": "card_skins",
	"booster": "boosters", "vip": "vip_packages", "tier": "table_tiers",
}

// catalogCols ستون‌های قابلِ ویرایشِ هر نوع (به‌جز sku که خودکار است).
var catalogCols = map[string][]string{
	"coin":    {"id", "title", "coins", "vip_days", "price_toman", "bonus_pct", "sort", "enabled"},
	"ticket":  {"id", "title", "tickets", "price_toman", "sort", "enabled"},
	"card":    {"id", "title", "price_coins", "sort", "enabled"},
	"booster": {"id", "title", "multiplier", "hours", "price_toman", "sort", "enabled"},
	"vip":     {"id", "title", "months", "price_toman", "sort", "enabled"},
	"tier":    {"id", "title", "hands", "entry", "prize", "xp", "trophy", "rank_reward", "sort", "enabled"},
}

var catalogHasSKU = map[string]bool{"coin": true, "ticket": true, "booster": true, "vip": true}

func (h *Handler) apiCatalog(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	httpx.JSON(w, http.StatusOK, map[string]any{
		"coin": h.rows(ctx, `SELECT id,title,coins,vip_days,price_toman,bonus_pct,sku,sort,enabled FROM coin_packages ORDER BY sort`,
			"id", "title", "coins", "vip_days", "price_toman", "bonus_pct", "sku", "sort", "enabled"),
		"ticket": h.rows(ctx, `SELECT id,title,tickets,price_toman,sku,sort,enabled FROM ticket_packages ORDER BY sort`,
			"id", "title", "tickets", "price_toman", "sku", "sort", "enabled"),
		"card": h.rows(ctx, `SELECT id,title,price_coins,sort,enabled FROM card_skins ORDER BY sort`,
			"id", "title", "price_coins", "sort", "enabled"),
		"booster": h.rows(ctx, `SELECT id,title,multiplier,hours,price_toman,sku,sort,enabled FROM boosters ORDER BY sort`,
			"id", "title", "multiplier", "hours", "price_toman", "sku", "sort", "enabled"),
		"vip": h.rows(ctx, `SELECT id,title,months,price_toman,sku,sort,enabled FROM vip_packages ORDER BY sort`,
			"id", "title", "months", "price_toman", "sku", "sort", "enabled"),
		"tier": h.rows(ctx, `SELECT id,title,hands,entry,prize,xp,trophy,rank_reward,sort,enabled FROM table_tiers ORDER BY sort`,
			"id", "title", "hands", "entry", "prize", "xp", "trophy", "rank_reward", "sort", "enabled"),
	})
}

// apiCatalogUpsert یک آیتم را می‌سازد یا به‌روزرسانی می‌کند (INSERT OR REPLACE).
func (h *Handler) apiCatalogUpsert(w http.ResponseWriter, r *http.Request) {
	kind := chi.URLParam(r, "kind")
	table, ok := catalogTable[kind]
	if !ok {
		httpx.Error(w, http.StatusBadRequest, "unknown kind")
		return
	}
	var body map[string]any
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		httpx.Error(w, http.StatusBadRequest, "invalid body")
		return
	}
	id, _ := body["id"].(string)
	if strings.TrimSpace(id) == "" {
		httpx.Error(w, http.StatusBadRequest, "id required")
		return
	}

	cols := append([]string{}, catalogCols[kind]...)
	vals := make([]any, 0, len(cols)+1)
	for _, c := range cols {
		vals = append(vals, coerceCol(c, body[c]))
	}
	// SKU: در ساخت خودکار تولید و در ویرایش حفظ می‌شود.
	if catalogHasSKU[kind] {
		var sku string
		_ = h.db.QueryRowContext(r.Context(),
			fmt.Sprintf(`SELECT sku FROM %s WHERE id=?`, table), id).Scan(&sku)
		if sku == "" {
			sku = newUUID()
		}
		cols = append(cols, "sku")
		vals = append(vals, sku)
	}

	ph := strings.TrimRight(strings.Repeat("?,", len(cols)), ",")
	q := fmt.Sprintf(`INSERT OR REPLACE INTO %s (%s) VALUES (%s)`, table, strings.Join(cols, ","), ph)
	if _, err := h.db.ExecContext(r.Context(), q, vals...); err != nil {
		httpx.Error(w, http.StatusInternalServerError, err.Error())
		return
	}
	_ = h.eco.LoadCatalog(r.Context())
	httpx.JSON(w, http.StatusOK, map[string]any{"ok": true})
}

func (h *Handler) apiCatalogDelete(w http.ResponseWriter, r *http.Request) {
	kind := chi.URLParam(r, "kind")
	table, ok := catalogTable[kind]
	if !ok {
		httpx.Error(w, http.StatusBadRequest, "unknown kind")
		return
	}
	id := r.URL.Query().Get("id")
	if _, err := h.db.ExecContext(r.Context(),
		fmt.Sprintf(`DELETE FROM %s WHERE id=?`, table), id); err != nil {
		httpx.Error(w, http.StatusInternalServerError, err.Error())
		return
	}
	_ = h.eco.LoadCatalog(r.Context())
	httpx.JSON(w, http.StatusOK, map[string]any{"ok": true})
}

// --- کاربران ---

func (h *Handler) apiUsers(w http.ResponseWriter, r *http.Request) {
	q := strings.TrimSpace(r.URL.Query().Get("q"))
	ctx := r.Context()
	var rows []map[string]any
	if q == "" {
		rows = h.rows(ctx,
			`SELECT id,mobile,first_name,coins,rank_points,is_admin FROM users ORDER BY id DESC LIMIT 50`,
			"id", "mobile", "first_name", "coins", "rank_points", "is_admin")
	} else {
		like := "%" + q + "%"
		rows = h.rowsArgs(ctx,
			`SELECT id,mobile,first_name,coins,rank_points,is_admin FROM users
			 WHERE mobile LIKE ? OR first_name LIKE ? OR CAST(id AS TEXT)=? ORDER BY id DESC LIMIT 50`,
			[]any{like, like, q},
			"id", "mobile", "first_name", "coins", "rank_points", "is_admin")
	}
	httpx.JSON(w, http.StatusOK, map[string]any{"users": rows})
}

func (h *Handler) apiUserCoins(w http.ResponseWriter, r *http.Request) {
	var b struct {
		UserID int64 `json:"user_id"`
		Amount int64 `json:"amount"`
	}
	if err := json.NewDecoder(r.Body).Decode(&b); err != nil || b.UserID == 0 {
		httpx.Error(w, http.StatusBadRequest, "invalid body")
		return
	}
	if err := h.eco.Adjust(r.Context(), b.UserID, economy.CurrencyCoin, b.Amount, "admin_adjust", "panel"); err != nil {
		httpx.Error(w, http.StatusBadRequest, err.Error())
		return
	}
	httpx.JSON(w, http.StatusOK, map[string]any{"ok": true})
}

func (h *Handler) apiUserGrant(w http.ResponseWriter, r *http.Request) {
	var b struct {
		UserID  int64  `json:"user_id"`
		Kind    string `json:"kind"` // coin | ticket | vip
		Amount  int64  `json:"amount"`
		VIPDays int    `json:"vip_days"`
	}
	if err := json.NewDecoder(r.Body).Decode(&b); err != nil || b.UserID == 0 {
		httpx.Error(w, http.StatusBadRequest, "invalid body")
		return
	}
	ctx := r.Context()
	var err error
	switch b.Kind {
	case "coin":
		err = h.eco.Adjust(ctx, b.UserID, economy.CurrencyCoin, b.Amount, "admin_grant", "panel")
	case "ticket":
		err = h.eco.Adjust(ctx, b.UserID, economy.CurrencyTicket, b.Amount, "admin_grant", "panel")
	case "vip":
		_, err = h.db.ExecContext(ctx,
			`UPDATE users SET vip_until = datetime('now', ?) WHERE id = ?`,
			fmt.Sprintf("+%d days", b.VIPDays), b.UserID)
	default:
		httpx.Error(w, http.StatusBadRequest, "unknown kind")
		return
	}
	if err != nil {
		httpx.Error(w, http.StatusBadRequest, err.Error())
		return
	}
	httpx.JSON(w, http.StatusOK, map[string]any{"ok": true})
}

// --- فرش‌ها ---

func (h *Handler) apiCarpets(w http.ResponseWriter, r *http.Request) {
	httpx.JSON(w, http.StatusOK, map[string]any{
		"carpets": h.rows(r.Context(),
			`SELECT id,title,price_coins,vip,sort,enabled FROM carpets ORDER BY sort`,
			"id", "title", "price_coins", "vip", "sort", "enabled"),
	})
}

func (h *Handler) apiCarpetUpsert(w http.ResponseWriter, r *http.Request) {
	var b struct {
		ID         string `json:"id"`
		Title      string `json:"title"`
		PriceCoins int64  `json:"price_coins"`
		VIP        bool   `json:"vip"`
		Sort       int    `json:"sort"`
	}
	if err := json.NewDecoder(r.Body).Decode(&b); err != nil || strings.TrimSpace(b.ID) == "" ||
		strings.ContainsAny(b.ID, `/\.`) {
		httpx.Error(w, http.StatusBadRequest, "invalid body")
		return
	}
	vip := 0
	if b.VIP {
		vip = 1
	}
	if _, err := h.db.ExecContext(r.Context(),
		`INSERT OR REPLACE INTO carpets (id,title,price_coins,vip,sort,enabled) VALUES (?,?,?,?,?,1)`,
		b.ID, b.Title, b.PriceCoins, vip, b.Sort); err != nil {
		httpx.Error(w, http.StatusInternalServerError, err.Error())
		return
	}
	httpx.JSON(w, http.StatusOK, map[string]any{"ok": true})
}

func (h *Handler) apiCarpetDelete(w http.ResponseWriter, r *http.Request) {
	_, _ = h.db.ExecContext(r.Context(), `DELETE FROM carpets WHERE id=?`, r.URL.Query().Get("id"))
	httpx.JSON(w, http.StatusOK, map[string]any{"ok": true})
}

// --- چت ---

func (h *Handler) apiChat(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	httpx.JSON(w, http.StatusOK, map[string]any{
		"packs": h.rows(ctx,
			`SELECT id,title,kind,price_coins,vip,sort,enabled FROM chat_packs ORDER BY sort`,
			"id", "title", "kind", "price_coins", "vip", "sort", "enabled"),
		"messages": h.rows(ctx,
			`SELECT id,pack_id,body,sort FROM chat_messages ORDER BY pack_id,sort`,
			"id", "pack_id", "body", "sort"),
	})
}

func (h *Handler) apiChatPackUpsert(w http.ResponseWriter, r *http.Request) {
	var b struct {
		ID         string `json:"id"`
		Title      string `json:"title"`
		Kind       string `json:"kind"`
		PriceCoins int64  `json:"price_coins"`
		VIP        bool   `json:"vip"`
		Sort       int    `json:"sort"`
	}
	if err := json.NewDecoder(r.Body).Decode(&b); err != nil || strings.TrimSpace(b.ID) == "" {
		httpx.Error(w, http.StatusBadRequest, "invalid body")
		return
	}
	if b.Kind != "emoji" {
		b.Kind = "text"
	}
	vip := 0
	if b.VIP {
		vip = 1
	}
	if _, err := h.db.ExecContext(r.Context(),
		`INSERT OR REPLACE INTO chat_packs (id,title,kind,price_coins,vip,sort,enabled) VALUES (?,?,?,?,?,?,1)`,
		b.ID, b.Title, b.Kind, b.PriceCoins, vip, b.Sort); err != nil {
		httpx.Error(w, http.StatusInternalServerError, err.Error())
		return
	}
	httpx.JSON(w, http.StatusOK, map[string]any{"ok": true})
}

func (h *Handler) apiChatPackDelete(w http.ResponseWriter, r *http.Request) {
	id := r.URL.Query().Get("id")
	_, _ = h.db.ExecContext(r.Context(), `DELETE FROM chat_messages WHERE pack_id=?`, id)
	_, _ = h.db.ExecContext(r.Context(), `DELETE FROM chat_packs WHERE id=?`, id)
	httpx.JSON(w, http.StatusOK, map[string]any{"ok": true})
}

func (h *Handler) apiChatMessageAdd(w http.ResponseWriter, r *http.Request) {
	var b struct {
		PackID string `json:"pack_id"`
		Body   string `json:"body"`
		Sort   int    `json:"sort"`
	}
	if err := json.NewDecoder(r.Body).Decode(&b); err != nil || b.PackID == "" || b.Body == "" {
		httpx.Error(w, http.StatusBadRequest, "invalid body")
		return
	}
	if _, err := h.db.ExecContext(r.Context(),
		`INSERT INTO chat_messages (pack_id,body,sort) VALUES (?,?,?)`, b.PackID, b.Body, b.Sort); err != nil {
		httpx.Error(w, http.StatusInternalServerError, err.Error())
		return
	}
	httpx.JSON(w, http.StatusOK, map[string]any{"ok": true})
}

func (h *Handler) apiChatMessageDelete(w http.ResponseWriter, r *http.Request) {
	id, _ := strconv.ParseInt(r.URL.Query().Get("id"), 10, 64)
	_, _ = h.db.ExecContext(r.Context(), `DELETE FROM chat_messages WHERE id=?`, id)
	httpx.JSON(w, http.StatusOK, map[string]any{"ok": true})
}

// --- تورنومنت‌ها ---

func (h *Handler) apiTournaments(w http.ResponseWriter, r *http.Request) {
	list, _ := h.eco.AdminListTournaments(r.Context())
	httpx.JSON(w, http.StatusOK, map[string]any{"tournaments": list})
}

type tournamentBody struct {
	ID          int64   `json:"id"`
	Title       string  `json:"title"`
	Description string  `json:"description"`
	EntryFee    int64   `json:"entry_fee"`
	Prizes      []int64 `json:"prizes"`
	StartsAt    string  `json:"starts_at"` // datetime-local به وقتِ تهران
	EndsAt      string  `json:"ends_at"`
}

// tournamentFromBody بدنه را به economy.Tournament (با زمانِ UTC) تبدیل می‌کند.
func tournamentFromBody(b tournamentBody) economy.Tournament {
	iran := time.FixedZone("IRST", 12600)
	parse := func(s string) time.Time {
		t, err := time.ParseInLocation("2006-01-02T15:04", s, iran)
		if err != nil {
			return time.Now().UTC()
		}
		return t.UTC()
	}
	return economy.Tournament{
		Title: b.Title, Description: b.Description, EntryFee: b.EntryFee,
		Prizes: b.Prizes, StartsAt: parse(b.StartsAt), EndsAt: parse(b.EndsAt),
	}
}

func (h *Handler) apiTournamentAdd(w http.ResponseWriter, r *http.Request) {
	var b tournamentBody
	if err := json.NewDecoder(r.Body).Decode(&b); err != nil || strings.TrimSpace(b.Title) == "" {
		httpx.Error(w, http.StatusBadRequest, "invalid body")
		return
	}
	if _, err := h.eco.CreateTournament(r.Context(), tournamentFromBody(b)); err != nil {
		httpx.Error(w, http.StatusInternalServerError, err.Error())
		return
	}
	httpx.JSON(w, http.StatusOK, map[string]any{"ok": true})
}

func (h *Handler) apiTournamentUpdate(w http.ResponseWriter, r *http.Request) {
	var b tournamentBody
	if err := json.NewDecoder(r.Body).Decode(&b); err != nil || b.ID == 0 || strings.TrimSpace(b.Title) == "" {
		httpx.Error(w, http.StatusBadRequest, "invalid body")
		return
	}
	if err := h.eco.UpdateTournament(r.Context(), b.ID, tournamentFromBody(b)); err != nil {
		httpx.Error(w, http.StatusInternalServerError, err.Error())
		return
	}
	httpx.JSON(w, http.StatusOK, map[string]any{"ok": true})
}

func (h *Handler) apiTournamentDelete(w http.ResponseWriter, r *http.Request) {
	id, _ := strconv.ParseInt(r.URL.Query().Get("id"), 10, 64)
	_ = h.eco.DeleteTournament(r.Context(), id)
	httpx.JSON(w, http.StatusOK, map[string]any{"ok": true})
}

func (h *Handler) apiSeasonReset(w http.ResponseWriter, r *http.Request) {
	if err := h.eco.ResetSeason(r.Context()); err != nil {
		httpx.Error(w, http.StatusInternalServerError, err.Error())
		return
	}
	httpx.JSON(w, http.StatusOK, map[string]any{"ok": true})
}

// coerceCol مقدارِ JSON را برای ستونِ کاتالوگ به نوعِ درست تبدیل می‌کند.
func coerceCol(col string, v any) any {
	switch col {
	case "id", "title":
		s, _ := v.(string)
		return s
	case "enabled":
		return boolToInt(v)
	default: // اعداد
		return toInt(v)
	}
}

func toInt(v any) int64 {
	switch n := v.(type) {
	case float64:
		return int64(n)
	case int64:
		return n
	case int:
		return int64(n)
	case string:
		i, _ := strconv.ParseInt(strings.TrimSpace(n), 10, 64)
		return i
	}
	return 0
}

func boolToInt(v any) int {
	switch b := v.(type) {
	case bool:
		if b {
			return 1
		}
	case float64:
		if b != 0 {
			return 1
		}
	case string:
		if b == "true" || b == "1" || b == "on" {
			return 1
		}
	}
	return 0
}
