package httpapi

import (
	"encoding/json"
	"errors"
	"net/http"
	"strings"
	"time"

	"github.com/easyai/easyai-ai-gateway/apps/api/internal/store"
)

// listPricingRuleSets godoc
// @Summary 列出定价规则集
// @Description 管理端返回可分配给平台、模型、租户或用户组的定价规则集。
// @Tags pricing
// @Produce json
// @Security BearerAuth
// @Success 200 {object} PricingRuleSetListResponse
// @Failure 401 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope
// @Failure 500 {object} ErrorEnvelope
// @Router /api/admin/pricing/rule-sets [get]
func (s *Server) listPricingRuleSets(w http.ResponseWriter, r *http.Request) {
	items, err := s.store.ListPricingRuleSets(r.Context())
	if err != nil {
		s.logger.Error("list pricing rule sets failed", "error", err)
		writeError(w, http.StatusInternalServerError, "list pricing rule sets failed")
		return
	}
	writeJSON(w, http.StatusOK, map[string]any{"items": items})
}

// createPricingRuleSet godoc
// @Summary 创建定价规则集
// @Description 管理端创建定价规则集，ruleSetKey、name 和至少一条 rule 必填。
// @Tags pricing
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param input body store.PricingRuleSetInput true "定价规则集请求"
// @Success 201 {object} store.PricingRuleSet
// @Failure 400 {object} ErrorEnvelope
// @Failure 401 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope
// @Failure 409 {object} ErrorEnvelope
// @Failure 500 {object} ErrorEnvelope
// @Router /api/admin/pricing/rule-sets [post]
func (s *Server) createPricingRuleSet(w http.ResponseWriter, r *http.Request) {
	var input store.PricingRuleSetInput
	if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
		writeError(w, http.StatusBadRequest, "invalid json body")
		return
	}
	if !validPricingRuleSetInput(input) {
		writeError(w, http.StatusBadRequest, "ruleSetKey, name and at least one rule are required")
		return
	}
	item, err := s.store.CreatePricingRuleSet(r.Context(), input)
	if err != nil {
		if store.IsUniqueViolation(err) {
			writeError(w, http.StatusConflict, "pricing rule set key already exists")
			return
		}
		s.logger.Error("create pricing rule set failed", "error", err)
		writeError(w, http.StatusInternalServerError, "create pricing rule set failed")
		return
	}
	writeJSON(w, http.StatusCreated, item)
}

// updatePricingRuleSet godoc
// @Summary 更新定价规则集
// @Description 管理端更新定价规则集及其规则列表。
// @Tags pricing
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param ruleSetID path string true "定价规则集 ID"
// @Param input body store.PricingRuleSetInput true "定价规则集请求"
// @Success 200 {object} store.PricingRuleSet
// @Failure 400 {object} ErrorEnvelope
// @Failure 401 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope
// @Failure 404 {object} ErrorEnvelope
// @Failure 409 {object} ErrorEnvelope
// @Failure 500 {object} ErrorEnvelope
// @Router /api/admin/pricing/rule-sets/{ruleSetID} [patch]
func (s *Server) updatePricingRuleSet(w http.ResponseWriter, r *http.Request) {
	var input store.PricingRuleSetInput
	if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
		writeError(w, http.StatusBadRequest, "invalid json body")
		return
	}
	if !validPricingRuleSetInput(input) {
		writeError(w, http.StatusBadRequest, "ruleSetKey, name and at least one rule are required")
		return
	}
	item, err := s.store.UpdatePricingRuleSet(r.Context(), r.PathValue("ruleSetID"), input)
	if err != nil {
		if store.IsNotFound(err) {
			writeError(w, http.StatusNotFound, "pricing rule set not found")
			return
		}
		if store.IsUniqueViolation(err) {
			writeError(w, http.StatusConflict, "pricing rule set key already exists")
			return
		}
		s.logger.Error("update pricing rule set failed", "error", err)
		writeError(w, http.StatusInternalServerError, "update pricing rule set failed")
		return
	}
	writeJSON(w, http.StatusOK, item)
}

// deletePricingRuleSet godoc
// @Summary 删除定价规则集
// @Description 管理端删除非默认定价规则集；默认规则集受保护。
// @Tags pricing
// @Produce json
// @Security BearerAuth
// @Param ruleSetID path string true "定价规则集 ID"
// @Success 204 "No Content"
// @Failure 401 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope
// @Failure 404 {object} ErrorEnvelope
// @Failure 500 {object} ErrorEnvelope
// @Router /api/admin/pricing/rule-sets/{ruleSetID} [delete]
func (s *Server) deletePricingRuleSet(w http.ResponseWriter, r *http.Request) {
	if err := s.store.DeletePricingRuleSet(r.Context(), r.PathValue("ruleSetID")); err != nil {
		if store.IsNotFound(err) {
			writeError(w, http.StatusNotFound, "pricing rule set not found")
			return
		}
		if errors.Is(err, store.ErrProtectedDefault) {
			writeError(w, http.StatusForbidden, "default pricing rule set cannot be deleted")
			return
		}
		s.logger.Error("delete pricing rule set failed", "error", err)
		writeError(w, http.StatusInternalServerError, "delete pricing rule set failed")
		return
	}
	w.WriteHeader(http.StatusNoContent)
}

func validPricingRuleSetInput(input store.PricingRuleSetInput) bool {
	if strings.TrimSpace(input.RuleSetKey) == "" || strings.TrimSpace(input.Name) == "" || len(input.Rules) == 0 {
		return false
	}
	if currency := strings.TrimSpace(input.Currency); currency != "" && currency != "resource" {
		return false
	}
	for _, rule := range input.Rules {
		if strings.TrimSpace(rule.ResourceType) == "" || strings.TrimSpace(rule.Unit) == "" {
			return false
		}
		if rule.BasePrice < 0 || (rule.BasePrice == 0 && !rule.IsFree) {
			return false
		}
		if currency := strings.TrimSpace(rule.Currency); currency != "" && currency != "resource" {
			return false
		}
		switch calculator := strings.TrimSpace(rule.CalculatorType); calculator {
		case "", "token_usage", "unit_weight", "duration_weight", "transition_matrix":
		default:
			return false
		}
		calculator := strings.TrimSpace(rule.CalculatorType)
		if calculator == "" {
			calculator = store.DefaultEffectivePricingCalculator(rule.ResourceType)
		}
		if store.ValidateEffectivePricingRuleShape(strings.TrimSpace(rule.ResourceType), store.NormalizeEffectivePricingRuleUnit(rule.ResourceType, rule.Unit), calculator) != nil {
			return false
		}
		effectiveFrom, fromOK := pricingEffectiveTime(rule.EffectiveFrom)
		effectiveTo, toOK := pricingEffectiveTime(rule.EffectiveTo)
		if !fromOK || !toOK || (!effectiveFrom.IsZero() && !effectiveTo.IsZero() && !effectiveFrom.Before(effectiveTo)) {
			return false
		}
	}
	return true
}

func pricingEffectiveTime(value string) (time.Time, bool) {
	value = strings.TrimSpace(value)
	if value == "" {
		return time.Time{}, true
	}
	parsed, err := time.Parse(time.RFC3339, value)
	return parsed, err == nil
}
