package achievementconfig

import (
	"strings"
	"time"

	"hyapp-admin-server/internal/appctx"
	"hyapp-admin-server/internal/integration/activityclient"
	"hyapp-admin-server/internal/middleware"
	"hyapp-admin-server/internal/modules/shared"
	"hyapp-admin-server/internal/response"
	activityv1 "hyapp.local/api/proto/activity/v1"

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

type Handler struct {
	activity activityclient.Client
	audit    shared.OperationLogger
}

func New(activity activityclient.Client, audit shared.OperationLogger) *Handler {
	return &Handler{activity: activity, audit: audit}
}

type achievementRequest struct {
	CollectionID           string             `json:"collection_id"`
	CollectionType         string             `json:"collection_type"`
	AchievementType        string             `json:"achievement_type"`
	Title                  string             `json:"title"`
	Description            string             `json:"description"`
	Status                 string             `json:"status"`
	PrimaryBadgeResourceID int64              `json:"primary_badge_resource_id"`
	RewardResourceGroupID  int64              `json:"reward_resource_group_id"`
	AutoPinPolicy          string             `json:"auto_pin_policy"`
	HonorVisibility        bool               `json:"honor_visibility"`
	EffectiveFromMS        int64              `json:"effective_from_ms"`
	EffectiveToMS          int64              `json:"effective_to_ms"`
	SortOrder              int32              `json:"sort_order"`
	DisplayConfigJSON      string             `json:"display_config_json"`
	Conditions             []conditionRequest `json:"conditions"`
}

type conditionRequest struct {
	MetricType          string `json:"metric_type"`
	TargetValue         int64  `json:"target_value"`
	TargetUnit          string `json:"target_unit"`
	DimensionFilterJSON string `json:"dimension_filter_json"`
}

type achievementDTO struct {
	AchievementID          string         `json:"achievement_id"`
	CollectionID           string         `json:"collection_id"`
	CollectionType         string         `json:"collection_type"`
	AchievementType        string         `json:"achievement_type"`
	Title                  string         `json:"title"`
	Description            string         `json:"description"`
	Status                 string         `json:"status"`
	Version                int64          `json:"version"`
	PrimaryBadgeResourceID int64          `json:"primary_badge_resource_id"`
	RewardResourceGroupID  int64          `json:"reward_resource_group_id"`
	AutoPinPolicy          string         `json:"auto_pin_policy"`
	HonorVisibility        bool           `json:"honor_visibility"`
	EffectiveFromMS        int64          `json:"effective_from_ms"`
	EffectiveToMS          int64          `json:"effective_to_ms"`
	SortOrder              int32          `json:"sort_order"`
	DisplayConfigJSON      string         `json:"display_config_json"`
	CreatedByAdminID       int64          `json:"created_by_admin_id"`
	UpdatedByAdminID       int64          `json:"updated_by_admin_id"`
	CreatedAtMS            int64          `json:"created_at_ms"`
	UpdatedAtMS            int64          `json:"updated_at_ms"`
	Conditions             []conditionDTO `json:"conditions"`
}

type conditionDTO struct {
	ConditionID         string `json:"condition_id"`
	AchievementID       string `json:"achievement_id"`
	Version             int64  `json:"version"`
	MetricType          string `json:"metric_type"`
	TargetValue         int64  `json:"target_value"`
	TargetUnit          string `json:"target_unit"`
	DimensionFilterJSON string `json:"dimension_filter_json"`
}

// ListAchievementDefinitions exposes the achievement rule catalog for operations; no user progress is mixed into this read.
func (h *Handler) ListAchievementDefinitions(c *gin.Context) {
	options := shared.ListOptions(c)
	resp, err := h.activity.ListAchievementDefinitions(c.Request.Context(), &activityv1.ListAchievementsRequest{
		Meta:           h.meta(c),
		CollectionType: strings.TrimSpace(firstQuery(c, "collection_type", "collectionType")),
		CollectionId:   strings.TrimSpace(firstQuery(c, "collection_id", "collectionId")),
		Page:           int32(options.Page),
		PageSize:       int32(options.PageSize),
	})
	if err != nil {
		response.ServerError(c, "获取成就配置失败")
		return
	}
	items := make([]achievementDTO, 0, len(resp.GetAchievements()))
	for _, item := range resp.GetAchievements() {
		items = append(items, achievementFromProto(item.GetDefinition()))
	}
	response.OK(c, response.Page{Items: items, Page: options.Page, PageSize: options.PageSize, Total: resp.GetTotal()})
}

// CreateAchievementDefinition writes one current-version achievement rule; reward badges are granted through the selected resource group.
func (h *Handler) CreateAchievementDefinition(c *gin.Context) {
	var req achievementRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "成就参数不正确")
		return
	}
	resp, err := h.activity.UpsertAchievementDefinition(c.Request.Context(), req.toProto(c, "", h.meta(c)))
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	item := achievementFromProto(resp.GetAchievement())
	h.auditLog(c, "create-achievement", item.AchievementID, item.Title)
	response.Created(c, item)
}

// UpdateAchievementDefinition rewrites the definition and condition set; activity-service owns versioning and unlock semantics.
func (h *Handler) UpdateAchievementDefinition(c *gin.Context) {
	achievementID := strings.TrimSpace(c.Param("achievement_id"))
	if achievementID == "" {
		response.BadRequest(c, "ID 参数不正确")
		return
	}
	var req achievementRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "成就参数不正确")
		return
	}
	resp, err := h.activity.UpsertAchievementDefinition(c.Request.Context(), req.toProto(c, achievementID, h.meta(c)))
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	item := achievementFromProto(resp.GetAchievement())
	h.auditLog(c, "update-achievement", item.AchievementID, item.Title)
	response.OK(c, item)
}

// DeleteAchievementDefinition 采用归档删除，避免后台删除动作破坏用户已获得徽章和历史成就事实。
func (h *Handler) DeleteAchievementDefinition(c *gin.Context) {
	achievementID := strings.TrimSpace(c.Param("achievement_id"))
	if achievementID == "" {
		response.BadRequest(c, "ID 参数不正确")
		return
	}
	resp, err := h.activity.DeleteAchievementDefinition(c.Request.Context(), &activityv1.DeleteAchievementDefinitionRequest{
		Meta:            h.meta(c),
		AchievementId:   achievementID,
		OperatorAdminId: int64(middleware.CurrentUserID(c)),
	})
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	item := achievementFromProto(resp.GetAchievement())
	h.auditLog(c, "delete-achievement", item.AchievementID, item.Title)
	response.OK(c, item)
}

func (r achievementRequest) toProto(c *gin.Context, achievementID string, meta *activityv1.RequestMeta) *activityv1.UpsertAchievementDefinitionRequest {
	conditions := make([]*activityv1.AchievementCondition, 0, len(r.Conditions))
	for _, condition := range r.Conditions {
		conditions = append(conditions, &activityv1.AchievementCondition{
			MetricType:          strings.TrimSpace(condition.MetricType),
			TargetValue:         condition.TargetValue,
			TargetUnit:          strings.TrimSpace(condition.TargetUnit),
			DimensionFilterJson: strings.TrimSpace(condition.DimensionFilterJSON),
		})
	}
	return &activityv1.UpsertAchievementDefinitionRequest{
		Meta:                   meta,
		AchievementId:          strings.TrimSpace(achievementID),
		CollectionId:           strings.TrimSpace(r.CollectionID),
		CollectionType:         strings.TrimSpace(r.CollectionType),
		AchievementType:        strings.TrimSpace(r.AchievementType),
		Title:                  strings.TrimSpace(r.Title),
		Description:            strings.TrimSpace(r.Description),
		Status:                 strings.TrimSpace(r.Status),
		PrimaryBadgeResourceId: r.PrimaryBadgeResourceID,
		RewardResourceGroupId:  r.RewardResourceGroupID,
		AutoPinPolicy:          strings.TrimSpace(r.AutoPinPolicy),
		HonorVisibility:        r.HonorVisibility,
		EffectiveFromMs:        r.EffectiveFromMS,
		EffectiveToMs:          r.EffectiveToMS,
		SortOrder:              r.SortOrder,
		DisplayConfigJson:      strings.TrimSpace(r.DisplayConfigJSON),
		OperatorAdminId:        int64(middleware.CurrentUserID(c)),
		Conditions:             conditions,
	}
}

func (h *Handler) meta(c *gin.Context) *activityv1.RequestMeta {
	return &activityv1.RequestMeta{
		RequestId: middleware.CurrentRequestID(c),
		Caller:    "admin-server",
		AppCode:   appctx.FromContext(c.Request.Context()),
		SentAtMs:  time.Now().UnixMilli(),
	}
}

func (h *Handler) auditLog(c *gin.Context, action string, achievementID string, detail string) {
	if h.audit == nil {
		return
	}
	shared.OperationLogWithResourceID(c, h.audit, action, "achievement_definitions", achievementID, "success", detail)
}

func achievementFromProto(item *activityv1.AchievementDefinition) achievementDTO {
	if item == nil {
		return achievementDTO{}
	}
	conditions := make([]conditionDTO, 0, len(item.GetConditions()))
	for _, condition := range item.GetConditions() {
		conditions = append(conditions, conditionFromProto(condition))
	}
	return achievementDTO{
		AchievementID:          item.GetAchievementId(),
		CollectionID:           item.GetCollectionId(),
		CollectionType:         item.GetCollectionType(),
		AchievementType:        item.GetAchievementType(),
		Title:                  item.GetTitle(),
		Description:            item.GetDescription(),
		Status:                 item.GetStatus(),
		Version:                item.GetVersion(),
		PrimaryBadgeResourceID: item.GetPrimaryBadgeResourceId(),
		RewardResourceGroupID:  item.GetRewardResourceGroupId(),
		AutoPinPolicy:          item.GetAutoPinPolicy(),
		HonorVisibility:        item.GetHonorVisibility(),
		EffectiveFromMS:        item.GetEffectiveFromMs(),
		EffectiveToMS:          item.GetEffectiveToMs(),
		SortOrder:              item.GetSortOrder(),
		DisplayConfigJSON:      item.GetDisplayConfigJson(),
		CreatedByAdminID:       item.GetCreatedByAdminId(),
		UpdatedByAdminID:       item.GetUpdatedByAdminId(),
		CreatedAtMS:            item.GetCreatedAtMs(),
		UpdatedAtMS:            item.GetUpdatedAtMs(),
		Conditions:             conditions,
	}
}

func conditionFromProto(item *activityv1.AchievementCondition) conditionDTO {
	if item == nil {
		return conditionDTO{}
	}
	return conditionDTO{
		ConditionID:         item.GetConditionId(),
		AchievementID:       item.GetAchievementId(),
		Version:             item.GetVersion(),
		MetricType:          item.GetMetricType(),
		TargetValue:         item.GetTargetValue(),
		TargetUnit:          item.GetTargetUnit(),
		DimensionFilterJSON: item.GetDimensionFilterJson(),
	}
}

func firstQuery(c *gin.Context, keys ...string) string {
	for _, key := range keys {
		if value := c.Query(key); value != "" {
			return value
		}
	}
	return ""
}
