package auth

import (
	"context"
	"encoding/json"
	"errors"
	"log/slog"
	"net/http"
	"regexp"
	"strings"
	"time"

	"hakemsho/internal/httpx"
	"hakemsho/internal/user"
)

// Handler endpointهای احراز هویت را فراهم می‌کند.
type Handler struct {
	users    *user.Repo
	otp      *OTPStore
	kave     *Kavenegar
	jwt      *JWT
	throttle *httpx.Throttle

	adminMobile string
	adminOTP    string
}

func NewHandler(users *user.Repo, otp *OTPStore, kave *Kavenegar, jwt *JWT, adminMobile, adminOTP string) *Handler {
	return &Handler{
		users:       users,
		otp:         otp,
		kave:        kave,
		jwt:         jwt,
		throttle:    httpx.NewThrottle(3, time.Minute), // معادل throttle:3,1
		adminMobile: adminMobile,
		adminOTP:    adminOTP,
	}
}

// mobileRe اعتبارسنجی ساده شماره موبایل ایران.
var mobileRe = regexp.MustCompile(`^09\d{9}$`)

type loginOTPReq struct {
	Mobile   string `json:"mobile"`
	FCMToken string `json:"fcm_token"`
}

// LoginOTP — POST /api/auth/login-otp
func (h *Handler) LoginOTP(w http.ResponseWriter, r *http.Request) {
	var req loginOTPReq
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		httpx.Error(w, http.StatusBadRequest, "invalid body")
		return
	}
	if !mobileRe.MatchString(req.Mobile) {
		httpx.Error(w, http.StatusUnprocessableEntity, "invalid mobile")
		return
	}
	// throttle بر اساس موبایل: حداکثر ۳ بار در دقیقه
	if !h.throttle.Allow(req.Mobile) {
		httpx.Error(w, http.StatusTooManyRequests, "too many requests")
		return
	}

	ctx := r.Context()
	u, err := h.users.FindOrCreate(ctx, req.Mobile)
	if err != nil {
		slog.Error("find or create user", "err", err)
		httpx.Error(w, http.StatusInternalServerError, "server error")
		return
	}

	// شماره ادمین کد ثابت دارد و SMS نمی‌گیرد
	if req.Mobile != h.adminMobile {
		code := Generate()
		if err := h.otp.Create(ctx, u.ID, code); err != nil {
			slog.Error("create otp", "err", err)
			httpx.Error(w, http.StatusInternalServerError, "server error")
			return
		}
		// ارسال SMS به‌صورت fire-and-forget (بدون صف جانبی، مطابق پلن مینیموم)
		go func(mobile, code string) {
			bg, cancel := context.WithTimeout(context.Background(), 10*time.Second)
			defer cancel()
			if err := h.kave.SendVerify(bg, mobile, code); err != nil {
				slog.Error("kavenegar send", "mobile", mobile, "err", err)
			}
		}(req.Mobile, code)
	}

	httpx.JSON(w, http.StatusOK, map[string]string{"message": "otp sent"})
}

type checkOTPReq struct {
	Mobile string `json:"mobile"`
	Token  string `json:"token"`
}

// CheckOTP — POST /api/auth/check-otp
func (h *Handler) CheckOTP(w http.ResponseWriter, r *http.Request) {
	var req checkOTPReq
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		httpx.Error(w, http.StatusBadRequest, "invalid body")
		return
	}
	if req.Mobile == "" || req.Token == "" {
		httpx.Error(w, http.StatusUnprocessableEntity, "mobile and token required")
		return
	}

	ctx := r.Context()
	u, err := h.users.FindByMobile(ctx, req.Mobile)
	if errors.Is(err, user.ErrNotFound) {
		httpx.Error(w, http.StatusNotFound, "user not found")
		return
	}
	if err != nil {
		httpx.Error(w, http.StatusInternalServerError, "server error")
		return
	}

	// راه ادمین برای تست بدون SMS
	adminBypass := h.adminMobile != "" && req.Mobile == h.adminMobile && req.Token == h.adminOTP
	if !adminBypass {
		if err := h.otp.Verify(ctx, u.ID, req.Token); err != nil {
			httpx.Error(w, http.StatusBadRequest, "token not valid")
			return
		}
	}

	ability := "user"
	if u.IsAdmin {
		ability = "admin"
	}
	token, err := h.jwt.Issue(u.ID, ability)
	if err != nil {
		httpx.Error(w, http.StatusInternalServerError, "server error")
		return
	}

	httpx.JSON(w, http.StatusOK, map[string]any{
		"user":  u,
		"token": token,
	})
}

// Me — GET /api/me (نیازمند JWT)
func (h *Handler) Me(w http.ResponseWriter, r *http.Request) {
	id, ok := UserID(r.Context())
	if !ok {
		httpx.Error(w, http.StatusUnauthorized, "unauthorized")
		return
	}
	u, err := h.users.FindByID(r.Context(), id)
	if err != nil {
		httpx.Error(w, http.StatusNotFound, "user not found")
		return
	}
	httpx.JSON(w, http.StatusOK, map[string]any{"user": u})
}

type profileReq struct {
	FirstName string `json:"first_name"`
	Avatar    string `json:"avatar"`
}

// UpdateProfile — POST /api/profile (تنظیم نام نمایشی و آواتار، نیازمند JWT)
func (h *Handler) UpdateProfile(w http.ResponseWriter, r *http.Request) {
	id, ok := UserID(r.Context())
	if !ok {
		httpx.Error(w, http.StatusUnauthorized, "unauthorized")
		return
	}
	var req profileReq
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		httpx.Error(w, http.StatusBadRequest, "invalid body")
		return
	}
	name := strings.TrimSpace(req.FirstName)
	if len([]rune(name)) < 2 || len([]rune(name)) > 20 {
		httpx.Error(w, http.StatusUnprocessableEntity, "name must be 2-20 characters")
		return
	}
	if err := h.users.UpdateProfile(r.Context(), id, name, req.Avatar); err != nil {
		httpx.Error(w, http.StatusInternalServerError, "server error")
		return
	}
	u, _ := h.users.FindByID(r.Context(), id)
	httpx.JSON(w, http.StatusOK, map[string]any{"user": u})
}
