package main

import (
	"context"
	"log/slog"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

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

	"hakemsho/internal/admin"
	"hakemsho/internal/auth"
	"hakemsho/internal/config"
	"hakemsho/internal/economy"
	"hakemsho/internal/httpx"
	"hakemsho/internal/static"
	"hakemsho/internal/store"
	"hakemsho/internal/telescope"
	"hakemsho/internal/user"
	"hakemsho/internal/ws"
)

func main() {
	slog.SetDefault(slog.New(slog.NewTextHandler(os.Stdout, nil)))
	cfg := config.Load()

	st, err := store.Open(cfg.DBPath)
	if err != nil {
		slog.Error("open db", "err", err)
		os.Exit(1)
	}
	defer st.Close()

	// لایه‌های دامنه
	users := user.NewRepo(st.DB)
	otp := auth.NewOTPStore(st.DB, cfg.OTPTTL)
	kave := auth.NewKavenegar(cfg.KaveAPIKey, cfg.KaveTemple)
	jwt := auth.NewJWT(cfg.JWTSecret, cfg.JWTTTL)
	authH := auth.NewHandler(users, otp, kave, jwt, cfg.AdminMobile, cfg.AdminOTP)

	// تلسکوپ: بازرسِ درون‌برنامه‌ایِ درخواست‌ها. شناسه‌ی کاربر را ارزان (فقط HMAC)
	// از توکن درمی‌آورد و شماره‌ی موبایل را در زمانِ نمایش (با DB) resolve می‌کند.
	scope := telescope.New(cfg.TelescopeSize,
		func(r *http.Request) int64 {
			tok := strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ")
			if tok == "" || tok == r.Header.Get("Authorization") {
				tok = r.URL.Query().Get("token")
			}
			if tok == "" {
				return 0
			}
			id, err := jwt.Verify(tok)
			if err != nil {
				return 0
			}
			return id
		},
		func(id int64) string {
			if u, err := users.FindByID(context.Background(), id); err == nil {
				return u.Mobile
			}
			return ""
		},
	)

	// اقتصاد و فروشگاه. تأییدِ خرید: مایکت (X-Access-Token) و کافه‌بازار (امضای RSA).
	// اگر کلیدها تنظیم نشده باشند، به تأییدکننده‌ی توسعه برمی‌گردیم (dev fallback).
	iap := economy.StoreRouter{DevFallback: true}
	// مایکت: تأییدِ آفلاینِ RSA (اگر کلید باشد) و/یا سمت‌سرور با access token.
	if cfg.MyketRSAKey != "" || (cfg.MyketAccessToken != "" && cfg.IAPPackage != "") {
		mv, err := economy.NewMyketVerifier(cfg.IAPPackage, cfg.MyketAccessToken).WithRSAKey(cfg.MyketRSAKey)
		if err != nil {
			slog.Error("myket rsa key", "err", err)
			os.Exit(1)
		}
		iap.Myket = mv
		slog.Info("IAP myket configured", "package", cfg.IAPPackage, "rsa", cfg.MyketRSAKey != "", "server", cfg.MyketAccessToken != "")
	}
	if cfg.BazaarRSAKey != "" {
		bv, err := economy.NewBazaarRSAVerifier(cfg.BazaarRSAKey)
		if err != nil {
			slog.Error("bazaar rsa key", "err", err)
			os.Exit(1)
		}
		iap.Bazaar = bv
		slog.Info("IAP bazaar (RSA) configured")
	}
	eco := economy.New(st.DB, economy.DevAdVerifier{}, iap)
	if err := eco.LoadCatalog(context.Background()); err != nil {
		slog.Error("load catalog", "err", err)
		os.Exit(1)
	}
	ecoH := economy.NewHandler(eco)

	// هاب WebSocket برای بازی realtime
	hub := ws.NewHub(func(token string) (int64, string, error) {
		id, err := jwt.Verify(token)
		if err != nil {
			return 0, "", err
		}
		name := "بازیکن " + strconv.FormatInt(id, 10)
		if u, err := users.FindByID(context.Background(), id); err == nil {
			if u.FirstName != nil && *u.FirstName != "" {
				name = *u.FirstName
			} else {
				name = u.Mobile
			}
		}
		return id, name, nil
	})
	hub.SetSettler(gameSettler{eco: eco})
	go hub.Run()

	r := chi.NewRouter()
	r.Use(middleware.RequestID)
	r.Use(middleware.RealIP)
	r.Use(middleware.Recoverer)
	r.Use(middleware.Timeout(15 * time.Second))
	// بازرسِ درخواست‌ها: هر درخواستِ /api و /admin را در رینگ‌بافر ثبت می‌کند.
	r.Use(scope.Middleware)

	r.Get("/health", func(w http.ResponseWriter, _ *http.Request) {
		httpx.JSON(w, http.StatusOK, map[string]string{"status": "ok"})
	})

	// WebSocket بازی (احراز هویت با توکن در ?token= یا هدر Authorization)
	r.Get("/ws", hub.ServeWS)

	// تصاویرِ کارت (هر اسکین یک پوشه) — از backend سرویس می‌شود تا اپ سبک بماند.
	r.Handle("/cards/*", static.CardsHandler(cfg.CardsDir))
	// تصاویرِ فرش (به‌جای میز) — از پنل ادمین آپلود می‌شوند.
	r.Handle("/carpets/*", static.CarpetsHandler(cfg.CarpetsDir))

	// پنل ادمین (HTML سرور‌ساید، پشت Basic Auth)
	r.Mount("/admin", admin.New(st.DB, eco, cfg.CardsDir, cfg.CarpetsDir).Routes(cfg.AdminUser, cfg.AdminPass))

	// بازرسِ درخواست‌ها (Telescope-lite) — پشتِ همان Basic Auth ادمین.
	r.Group(func(r chi.Router) {
		r.Use(middleware.BasicAuth("hakemsho-admin", map[string]string{cfg.AdminUser: cfg.AdminPass}))
		r.Get("/admin/telescope", scope.Page)
		r.Get("/admin/telescope/data", scope.Data)
	})

	r.Route("/api", func(r chi.Router) {
		r.Route("/auth", func(r chi.Router) {
			r.Post("/login-otp", authH.LoginOTP)
			r.Post("/check-otp", authH.CheckOTP)
		})
		// مسیرهای محافظت‌شده با JWT
		r.Group(func(r chi.Router) {
			r.Use(jwt.Middleware)
			r.Get("/me", authH.Me)
			r.Post("/profile", authH.UpdateProfile)

			// کیف‌پول و فروشگاه
			r.Get("/wallet", ecoH.Wallet)
			r.Get("/stats", ecoH.Stats)
			r.Get("/leaderboard", ecoH.Leaderboard)
			r.Get("/tables/info", ecoH.TablesInfo)
			r.Get("/shop", ecoH.Shop)
			r.Post("/shop/buy-card", ecoH.BuyCard)
			r.Post("/shop/select-card", ecoH.SelectCard)
			r.Get("/chat-packs", ecoH.ChatPacks)
			r.Post("/shop/buy-chat-pack", ecoH.BuyChatPack)
			r.Get("/carpets", ecoH.Carpets)
			r.Post("/shop/buy-carpet", ecoH.BuyCarpet)
			r.Post("/shop/select-carpet", ecoH.SelectCarpet)
			r.Post("/shop/purchase", ecoH.Purchase)
			r.Post("/rewards/daily", ecoH.Daily)
			r.Post("/rewards/ad", ecoH.AdReward)

			// تورنومنت‌ها
			r.Get("/tournaments", ecoH.Tournaments)
			r.Get("/tournaments/standings", ecoH.TournamentStandings)
			r.Post("/tournaments/join", ecoH.JoinTournament)
		})
	})

	// تسویه‌ی خودکارِ تورنومنت‌های پایان‌یافته (هر دقیقه).
	go func() {
		t := time.NewTicker(time.Minute)
		defer t.Stop()
		for range t.C {
			eco.SettleTournaments(context.Background())
		}
	}()

	srv := &http.Server{
		Addr:         cfg.Addr,
		Handler:      r,
		ReadTimeout:  15 * time.Second,
		WriteTimeout: 15 * time.Second,
		IdleTimeout:  60 * time.Second,
	}
	slog.Info("server starting", "addr", cfg.Addr)
	if err := srv.ListenAndServe(); err != nil {
		slog.Error("server", "err", err)
		os.Exit(1)
	}
}
