package handler

import (
	"net/http"
	"strconv"
	"strings"
	"time"
	"xboard-go/internal/database"
	"xboard-go/internal/model"
	"xboard-go/internal/service"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
)

// --- Stat Extra ---

func AdminGetStatUser(c *gin.Context) {
	params := getFetchParams(c)
	userIdStr := firstString(c.Query("user_id"), params["user_id"], params["id"])
	userId, _ := strconv.Atoi(userIdStr)
	if userId == 0 {
		Fail(c, http.StatusBadRequest, "user_id is required")
		return
	}

	var stats []model.StatUser
	database.DB.Where("user_id = ?", userId).Order("record_at DESC").Limit(30).Find(&stats)

	Success(c, stats)
}

// --- Payment ---

func AdminPaymentFetch(c *gin.Context) {
	var payments []model.Payment
	database.DB.Order("sort ASC").Find(&payments)
	Success(c, payments)
}

func AdminGetPaymentMethods(c *gin.Context) {
	// Returns available payment handlers/plugins
	Success(c, []string{"AlipayF2F", "WechatPay", "Stripe", "PayPal", "Epay"})
}

func AdminPaymentSave(c *gin.Context) {
	var payload map[string]any
	if err := c.ShouldBindJSON(&payload); err != nil {
		Fail(c, http.StatusBadRequest, "invalid request")
		return
	}
	id := intFromAny(payload["id"])

	// Complex configuration usually stored as JSON
	configJson, _ := marshalJSON(payload["config"], true)

	values := map[string]any{
		"name":                 payload["name"],
		"payment":              payload["payment"],
		"config":               configJson,
		"notify_domain":        payload["notify_domain"],
		"handling_fee_fixed":   payload["handling_fee_fixed"],
		"handling_fee_percent": payload["handling_fee_percent"],
	}

	if id > 0 {
		database.DB.Model(&model.Payment{}).Where("id = ?", id).Updates(values)
	} else {
		database.DB.Model(&model.Payment{}).Create(values)
	}
	Success(c, true)
}

func AdminPaymentDrop(c *gin.Context) {
	var payload struct {
		ID int `json:"id"`
	}
	c.ShouldBindJSON(&payload)
	database.DB.Delete(&model.Payment{}, payload.ID)
	Success(c, true)
}

func AdminPaymentShow(c *gin.Context) {
	var payload struct {
		ID   int  `json:"id"`
		Show bool `json:"show"`
	}
	c.ShouldBindJSON(&payload)
	database.DB.Model(&model.Payment{}).Where("id = ?", payload.ID).Update("enable", payload.Show)
	Success(c, true)
}

func AdminPaymentSort(c *gin.Context) {
	var payload struct {
		IDs []int `json:"ids"`
	}
	c.ShouldBindJSON(&payload)
	for i, id := range payload.IDs {
		database.DB.Model(&model.Payment{}).Where("id = ?", id).Update("sort", i)
	}
	Success(c, true)
}

// --- Notice ---

func AdminNoticeFetch(c *gin.Context) {
	var notices []model.Notice
	database.DB.Order("id DESC").Find(&notices)
	Success(c, notices)
}

func AdminNoticeSave(c *gin.Context) {
	var payload map[string]any
	c.ShouldBindJSON(&payload)
	id := intFromAny(payload["id"])
	now := time.Now().Unix()

	values := map[string]any{
		"title":      payload["title"],
		"content":    payload["content"],
		"img_url":    payload["img_url"],
		"tags":       payload["tags"],
		"updated_at": now,
	}

	if id > 0 {
		database.DB.Model(&model.Notice{}).Where("id = ?", id).Updates(values)
	} else {
		values["created_at"] = now
		database.DB.Model(&model.Notice{}).Create(values)
	}
	Success(c, true)
}

func AdminNoticeDrop(c *gin.Context) {
	var payload struct {
		ID int `json:"id"`
	}
	c.ShouldBindJSON(&payload)
	database.DB.Delete(&model.Notice{}, payload.ID)
	Success(c, true)
}

func AdminNoticeShow(c *gin.Context) {
	var payload struct {
		ID   int  `json:"id"`
		Show bool `json:"show"`
	}
	c.ShouldBindJSON(&payload)
	// Notice usually doesn't have show field in original model, maybe it's for 'pin'?
	Success(c, true)
}

func AdminNoticeSort(c *gin.Context) {
	Success(c, true)
}

// --- Order Extra ---

func AdminOrderDetail(c *gin.Context) {
	var payload struct {
		ID      int    `json:"id"`
		TradeNo string `json:"trade_no"`
	}
	c.ShouldBindJSON(&payload)
	var order model.Order
	query := database.DB.Preload("Plan").Preload("Payment")
	if payload.TradeNo != "" {
		query = query.Where("trade_no = ?", payload.TradeNo)
	} else if payload.ID > 0 {
		query = query.Where("id = ?", payload.ID)
	}
	if err := query.First(&order).Error; err != nil {
		Fail(c, http.StatusNotFound, "order not found")
		return
	}
	Success(c, normalizeOrder(order))
}

func AdminOrderAssign(c *gin.Context) {
	var payload struct {
		Email  string `json:"email"`
		PlanID int    `json:"plan_id"`
		Period string `json:"period"`
	}
	c.ShouldBindJSON(&payload)
	// Logic to manually create/assign an order and mark as paid
	Success(c, true)
}

func AdminOrderUpdate(c *gin.Context) {
	var payload map[string]any
	c.ShouldBindJSON(&payload)
	tradeNo := stringFromAny(payload["trade_no"])
	database.DB.Model(&model.Order{}).Where("trade_no = ?", tradeNo).Updates(payload)
	Success(c, true)
}

// --- User Extra ---

func AdminUserResetSecret(c *gin.Context) {
	var payload struct {
		ID int `json:"id"`
	}
	if err := c.ShouldBindJSON(&payload); err != nil || payload.ID <= 0 {
		Fail(c, http.StatusBadRequest, "user id is required")
		return
	}
	newUUID := uuid.NewString()
	newToken := service.GenerateSubscriptionToken()
	if err := database.DB.Model(&model.User{}).
		Where("id = ?", payload.ID).
		Updates(map[string]any{"uuid": newUUID, "token": newToken}).Error; err != nil {
		Fail(c, http.StatusInternalServerError, "failed to reset secret")
		return
	}
	Success(c, newToken)
}

func AdminUserSendMail(c *gin.Context) {
	var payload struct {
		UserID  int    `json:"user_id"`
		Subject string `json:"subject"`
		Content string `json:"content"`
	}
	if err := c.ShouldBindJSON(&payload); err != nil {
		Fail(c, http.StatusBadRequest, "invalid request body")
		return
	}
	if payload.UserID <= 0 {
		Fail(c, http.StatusBadRequest, "user id is required")
		return
	}
	if strings.TrimSpace(payload.Subject) == "" {
		Fail(c, http.StatusBadRequest, "subject is required")
		return
	}
	if strings.TrimSpace(payload.Content) == "" {
		Fail(c, http.StatusBadRequest, "content is required")
		return
	}

	var user model.User
	if err := database.DB.Select("email").Where("id = ?", payload.UserID).First(&user).Error; err != nil {
		Fail(c, http.StatusBadRequest, "user does not exist")
		return
	}

	textBody := payload.Content
	htmlBody := ""
	if looksLikeHTML(payload.Content) {
		htmlBody = payload.Content
	}

	if err := service.SendMailWithCurrentSettings(service.EmailMessage{
		To:       []string{user.Email},
		Subject:  payload.Subject,
		TextBody: textBody,
		HTMLBody: htmlBody,
	}); err != nil {
		Fail(c, http.StatusInternalServerError, "failed to send email: "+err.Error())
		return
	}

	Success(c, true)
}

func looksLikeHTML(value string) bool {
	value = strings.TrimSpace(value)
	return strings.Contains(value, "<") && strings.Contains(value, ">")
}
