package controller

import (
	"net/http"
	"strconv"
	"strings"
	"unicode/utf8"

	"github.com/QuantumNous/new-api/common"
	"github.com/QuantumNous/new-api/i18n"
	"github.com/QuantumNous/new-api/model"

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

func GetAllRedemptions(c *gin.Context) {
	pageInfo := common.GetPageQuery(c)
	redemptions, total, err := model.GetAllRedemptions(pageInfo.GetStartIdx(), pageInfo.GetPageSize())
	if err != nil {
		common.ApiError(c, err)
		return
	}
	enrichRedemptions(redemptions)
	pageInfo.SetTotal(int(total))
	pageInfo.SetItems(redemptions)
	common.ApiSuccess(c, pageInfo)
}

func SearchRedemptions(c *gin.Context) {
	keyword := c.Query("keyword")
	pageInfo := common.GetPageQuery(c)
	redemptions, total, err := model.SearchRedemptions(keyword, pageInfo.GetStartIdx(), pageInfo.GetPageSize())
	if err != nil {
		common.ApiError(c, err)
		return
	}
	enrichRedemptions(redemptions)
	pageInfo.SetTotal(int(total))
	pageInfo.SetItems(redemptions)
	common.ApiSuccess(c, pageInfo)
}

func GetRedemption(c *gin.Context) {
	id, err := strconv.Atoi(c.Param("id"))
	if err != nil {
		common.ApiError(c, err)
		return
	}
	redemption, err := model.GetRedemptionById(id)
	if err != nil {
		common.ApiError(c, err)
		return
	}
	enrichRedemption(redemption)
	c.JSON(http.StatusOK, gin.H{
		"success": true,
		"message": "",
		"data":    redemption,
	})
}

func AddRedemption(c *gin.Context) {
	redemption := model.Redemption{}
	err := c.ShouldBindJSON(&redemption)
	if err != nil {
		common.ApiError(c, err)
		return
	}
	if !validateRedemptionPayload(c, &redemption, true) {
		return
	}
	if redemption.Count <= 0 {
		common.ApiErrorI18n(c, i18n.MsgRedemptionCountPositive)
		return
	}
	if redemption.Count > 100 {
		common.ApiErrorI18n(c, i18n.MsgRedemptionCountMax)
		return
	}
	var keys []string
	for i := 0; i < redemption.Count; i++ {
		key := common.GetUUID()
		cleanRedemption := model.Redemption{
			UserId:      c.GetInt("id"),
			Name:        redemption.Name,
			Key:         key,
			CreatedTime: common.GetTimestamp(),
			Quota:       redemption.Quota,
			RedeemType:  redemption.RedeemType,
			PlanId:      redemption.PlanId,
			SourceNote:  redemption.SourceNote,
			ExpiredTime: redemption.ExpiredTime,
		}
		err = cleanRedemption.Insert()
		if err != nil {
			common.SysError("failed to insert redemption: " + err.Error())
			c.JSON(http.StatusOK, gin.H{
				"success": false,
				"message": i18n.T(c, i18n.MsgRedemptionCreateFailed),
				"data":    keys,
			})
			return
		}
		keys = append(keys, key)
	}
	c.JSON(http.StatusOK, gin.H{
		"success": true,
		"message": "",
		"data":    keys,
	})
}

func DeleteRedemption(c *gin.Context) {
	id, _ := strconv.Atoi(c.Param("id"))
	err := model.DeleteRedemptionById(id)
	if err != nil {
		common.ApiError(c, err)
		return
	}
	c.JSON(http.StatusOK, gin.H{
		"success": true,
		"message": "",
	})
}

func UpdateRedemption(c *gin.Context) {
	statusOnly := c.Query("status_only")
	redemption := model.Redemption{}
	err := c.ShouldBindJSON(&redemption)
	if err != nil {
		common.ApiError(c, err)
		return
	}
	cleanRedemption, err := model.GetRedemptionById(redemption.Id)
	if err != nil {
		common.ApiError(c, err)
		return
	}
	if statusOnly == "" {
		if !validateRedemptionPayload(c, &redemption, true) {
			return
		}
		cleanRedemption.Name = redemption.Name
		cleanRedemption.Quota = redemption.Quota
		cleanRedemption.RedeemType = redemption.RedeemType
		cleanRedemption.PlanId = redemption.PlanId
		cleanRedemption.SourceNote = redemption.SourceNote
		cleanRedemption.ExpiredTime = redemption.ExpiredTime
	}
	if statusOnly != "" {
		cleanRedemption.Status = redemption.Status
	}
	err = cleanRedemption.Update()
	if err != nil {
		common.ApiError(c, err)
		return
	}
	enrichRedemption(cleanRedemption)
	c.JSON(http.StatusOK, gin.H{
		"success": true,
		"message": "",
		"data":    cleanRedemption,
	})
}

func DeleteInvalidRedemption(c *gin.Context) {
	rows, err := model.DeleteInvalidRedemptions()
	if err != nil {
		common.ApiError(c, err)
		return
	}
	c.JSON(http.StatusOK, gin.H{
		"success": true,
		"message": "",
		"data":    rows,
	})
}

func validateExpiredTime(c *gin.Context, expired int64) (bool, string) {
	if expired != 0 && expired < common.GetTimestamp() {
		return false, i18n.T(c, i18n.MsgRedemptionExpireTimeInvalid)
	}
	return true, ""
}

func validateRedemptionPayload(c *gin.Context, redemption *model.Redemption, requireName bool) bool {
	if redemption == nil {
		common.ApiErrorMsg(c, "invalid redemption payload")
		return false
	}
	redemption.Name = strings.TrimSpace(redemption.Name)
	redemption.SourceNote = strings.TrimSpace(redemption.SourceNote)
	redemption.RedeemType = model.NormalizeRedemptionType(redemption.RedeemType)
	if redemption.RedeemType == "" {
		common.ApiErrorMsg(c, "invalid redeem type")
		return false
	}
	if requireName && (utf8.RuneCountInString(redemption.Name) == 0 || utf8.RuneCountInString(redemption.Name) > 20) {
		common.ApiErrorI18n(c, i18n.MsgRedemptionNameLength)
		return false
	}
	if valid, msg := validateExpiredTime(c, redemption.ExpiredTime); !valid {
		c.JSON(http.StatusOK, gin.H{"success": false, "message": msg})
		return false
	}
	switch redemption.RedeemType {
	case model.RedemptionTypeQuota:
		if redemption.Quota <= 0 {
			common.ApiErrorMsg(c, "quota must be greater than 0")
			return false
		}
		redemption.PlanId = 0
	case model.RedemptionTypeSubscription:
		if redemption.PlanId <= 0 {
			common.ApiErrorMsg(c, "plan_id is required")
			return false
		}
		plan, err := model.GetSubscriptionPlanById(redemption.PlanId)
		if err != nil || plan == nil {
			common.ApiErrorMsg(c, "subscription plan not found")
			return false
		}
		redemption.Quota = 0
	}
	return true
}

func enrichRedemptions(redemptions []*model.Redemption) {
	if len(redemptions) == 0 {
		return
	}
	planTitles := make(map[int]string)
	for _, redemption := range redemptions {
		if redemption == nil || model.NormalizeRedemptionType(redemption.RedeemType) != model.RedemptionTypeSubscription || redemption.PlanId <= 0 {
			continue
		}
		if _, ok := planTitles[redemption.PlanId]; ok {
			redemption.PlanTitle = planTitles[redemption.PlanId]
			continue
		}
		plan, err := model.GetSubscriptionPlanById(redemption.PlanId)
		if err != nil || plan == nil {
			continue
		}
		planTitles[redemption.PlanId] = plan.Title
		redemption.PlanTitle = plan.Title
	}
}

func enrichRedemption(redemption *model.Redemption) {
	if redemption == nil || model.NormalizeRedemptionType(redemption.RedeemType) != model.RedemptionTypeSubscription || redemption.PlanId <= 0 {
		return
	}
	plan, err := model.GetSubscriptionPlanById(redemption.PlanId)
	if err != nil || plan == nil {
		return
	}
	redemption.PlanTitle = plan.Title
}
