package resource

import (
	"database/sql"
	"fmt"
	"strconv"
	"strings"

	"hyapp-admin-server/internal/appctx"
	"hyapp-admin-server/internal/integration/walletclient"
	"hyapp-admin-server/internal/middleware"
	"hyapp-admin-server/internal/modules/shared"
	"hyapp-admin-server/internal/repository"
	"hyapp-admin-server/internal/response"
	walletv1 "hyapp.local/api/proto/wallet/v1"

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

type Handler struct {
	wallet walletclient.Client
	store  *repository.Store
	userDB *sql.DB
	audit  shared.OperationLogger
}

func New(wallet walletclient.Client, store *repository.Store, userDB *sql.DB, audit shared.OperationLogger) *Handler {
	return &Handler{wallet: wallet, store: store, userDB: userDB, audit: audit}
}

func (h *Handler) ListResources(c *gin.Context) {
	options := shared.ListOptions(c)
	resp, err := h.wallet.ListResources(c.Request.Context(), &walletv1.ListResourcesRequest{
		RequestId:    middleware.CurrentRequestID(c),
		AppCode:      appctx.FromContext(c.Request.Context()),
		ResourceType: strings.TrimSpace(c.Query("resource_type")),
		Status:       options.Status,
		Keyword:      options.Keyword,
		Page:         int32(options.Page),
		PageSize:     int32(options.PageSize),
	})
	if err != nil {
		response.ServerError(c, "获取资源列表失败")
		return
	}
	items := make([]resourceDTO, 0, len(resp.GetResources()))
	for _, item := range resp.GetResources() {
		items = append(items, resourceFromProto(item))
	}
	response.OK(c, response.Page{Items: items, Page: options.Page, PageSize: options.PageSize, Total: resp.GetTotal()})
}

func (h *Handler) GetResource(c *gin.Context) {
	resourceID, ok := parseID(c, "resource_id")
	if !ok {
		return
	}
	resp, err := h.wallet.GetResource(c.Request.Context(), &walletv1.GetResourceRequest{
		RequestId:  middleware.CurrentRequestID(c),
		AppCode:    appctx.FromContext(c.Request.Context()),
		ResourceId: resourceID,
	})
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	response.OK(c, resourceFromProto(resp.GetResource()))
}

func (h *Handler) CreateResource(c *gin.Context) {
	var req resourceRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "资源参数不正确")
		return
	}
	resp, err := h.wallet.CreateResource(c.Request.Context(), req.createProto(c))
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	resource := resourceFromProto(resp.GetResource())
	h.auditLog(c, "create-resource", "resources", fmt.Sprintf("%d", resource.ResourceID), "success", resource.ResourceCode)
	response.Created(c, resource)
}

func (h *Handler) UpdateResource(c *gin.Context) {
	resourceID, ok := parseID(c, "resource_id")
	if !ok {
		return
	}
	var req resourceRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "资源参数不正确")
		return
	}
	protoReq := req.updateProto(c, resourceID)
	resp, err := h.wallet.UpdateResource(c.Request.Context(), protoReq)
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	resource := resourceFromProto(resp.GetResource())
	h.auditLog(c, "update-resource", "resources", fmt.Sprintf("%d", resource.ResourceID), "success", resource.ResourceCode)
	response.OK(c, resource)
}

func (h *Handler) EnableResource(c *gin.Context) {
	h.setResourceStatus(c, "active")
}

func (h *Handler) DisableResource(c *gin.Context) {
	h.setResourceStatus(c, "disabled")
}

func (h *Handler) ListResourceGroups(c *gin.Context) {
	options := shared.ListOptions(c)
	resp, err := h.wallet.ListResourceGroups(c.Request.Context(), &walletv1.ListResourceGroupsRequest{
		RequestId: middleware.CurrentRequestID(c),
		AppCode:   appctx.FromContext(c.Request.Context()),
		Status:    options.Status,
		Keyword:   options.Keyword,
		Page:      int32(options.Page),
		PageSize:  int32(options.PageSize),
	})
	if err != nil {
		response.ServerError(c, "获取资源组列表失败")
		return
	}
	items := make([]resourceGroupDTO, 0, len(resp.GetGroups()))
	for _, item := range resp.GetGroups() {
		items = append(items, resourceGroupFromProto(item))
	}
	response.OK(c, response.Page{Items: items, Page: options.Page, PageSize: options.PageSize, Total: resp.GetTotal()})
}

func (h *Handler) GetResourceGroup(c *gin.Context) {
	groupID, ok := parseID(c, "group_id")
	if !ok {
		return
	}
	resp, err := h.wallet.GetResourceGroup(c.Request.Context(), &walletv1.GetResourceGroupRequest{
		RequestId: middleware.CurrentRequestID(c),
		AppCode:   appctx.FromContext(c.Request.Context()),
		GroupId:   groupID,
	})
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	response.OK(c, resourceGroupFromProto(resp.GetGroup()))
}

func (h *Handler) CreateResourceGroup(c *gin.Context) {
	var req resourceGroupRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "资源组参数不正确")
		return
	}
	resp, err := h.wallet.CreateResourceGroup(c.Request.Context(), req.createProto(c))
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	group := resourceGroupFromProto(resp.GetGroup())
	h.auditLog(c, "create-resource-group", "resource_groups", fmt.Sprintf("%d", group.GroupID), "success", group.GroupCode)
	response.Created(c, group)
}

func (h *Handler) UpdateResourceGroup(c *gin.Context) {
	groupID, ok := parseID(c, "group_id")
	if !ok {
		return
	}
	var req resourceGroupRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "资源组参数不正确")
		return
	}
	resp, err := h.wallet.UpdateResourceGroup(c.Request.Context(), req.updateProto(c, groupID))
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	group := resourceGroupFromProto(resp.GetGroup())
	h.auditLog(c, "update-resource-group", "resource_groups", fmt.Sprintf("%d", group.GroupID), "success", group.GroupCode)
	response.OK(c, group)
}

func (h *Handler) EnableResourceGroup(c *gin.Context) {
	h.setResourceGroupStatus(c, "active")
}

func (h *Handler) DisableResourceGroup(c *gin.Context) {
	h.setResourceGroupStatus(c, "disabled")
}

func (h *Handler) ListGifts(c *gin.Context) {
	options := shared.ListOptions(c)
	regionID, filterRegion, ok := optionalInt64QueryWithPresence(c, "regionId", "region_id")
	if !ok {
		return
	}
	resp, err := h.wallet.ListGiftConfigs(c.Request.Context(), &walletv1.ListGiftConfigsRequest{
		RequestId:    middleware.CurrentRequestID(c),
		AppCode:      appctx.FromContext(c.Request.Context()),
		Status:       options.Status,
		Keyword:      options.Keyword,
		Page:         int32(options.Page),
		PageSize:     int32(options.PageSize),
		RegionId:     regionID,
		FilterRegion: filterRegion,
	})
	if err != nil {
		response.ServerError(c, "获取礼物列表失败")
		return
	}
	items := make([]giftDTO, 0, len(resp.GetGifts()))
	for _, item := range resp.GetGifts() {
		items = append(items, giftFromProto(item))
	}
	response.OK(c, response.Page{Items: items, Page: options.Page, PageSize: options.PageSize, Total: resp.GetTotal()})
}

func (h *Handler) CreateGift(c *gin.Context) {
	var req giftRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "礼物参数不正确")
		return
	}
	resp, err := h.wallet.CreateGiftConfig(c.Request.Context(), req.createProto(c))
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	gift := giftFromProto(resp.GetGift())
	h.auditLog(c, "create-gift", "gift_configs", gift.GiftID, "success", gift.Name)
	response.Created(c, gift)
}

func (h *Handler) UpdateGift(c *gin.Context) {
	giftID := strings.TrimSpace(c.Param("gift_id"))
	if giftID == "" {
		response.BadRequest(c, "ID 参数不正确")
		return
	}
	var req giftRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "礼物参数不正确")
		return
	}
	resp, err := h.wallet.UpdateGiftConfig(c.Request.Context(), req.updateProto(c, giftID))
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	gift := giftFromProto(resp.GetGift())
	h.auditLog(c, "update-gift", "gift_configs", gift.GiftID, "success", gift.Name)
	response.OK(c, gift)
}

func (h *Handler) EnableGift(c *gin.Context) {
	h.setGiftStatus(c, "active")
}

func (h *Handler) DisableGift(c *gin.Context) {
	h.setGiftStatus(c, "disabled")
}

func (h *Handler) GrantResource(c *gin.Context) {
	var req grantResourceRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "资源赠送参数不正确")
		return
	}
	resp, err := h.wallet.GrantResource(c.Request.Context(), &walletv1.GrantResourceRequest{
		CommandId:      strings.TrimSpace(req.CommandID),
		AppCode:        appctx.FromContext(c.Request.Context()),
		TargetUserId:   req.TargetUserID,
		ResourceId:     req.ResourceID,
		Quantity:       req.Quantity,
		DurationMs:     req.DurationMS,
		Reason:         strings.TrimSpace(req.Reason),
		OperatorUserId: actorID(c),
		GrantSource:    "admin",
	})
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	grant := grantFromProto(resp.GetGrant())
	h.auditLog(c, "grant-resource", "resource_grants", grant.GrantID, "success", fmt.Sprintf("target_user_id=%d", grant.TargetUserID))
	response.Created(c, grant)
}

func (h *Handler) GrantResourceGroup(c *gin.Context) {
	var req grantGroupRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "资源组赠送参数不正确")
		return
	}
	resp, err := h.wallet.GrantResourceGroup(c.Request.Context(), &walletv1.GrantResourceGroupRequest{
		CommandId:      strings.TrimSpace(req.CommandID),
		AppCode:        appctx.FromContext(c.Request.Context()),
		TargetUserId:   req.TargetUserID,
		GroupId:        req.GroupID,
		Reason:         strings.TrimSpace(req.Reason),
		OperatorUserId: actorID(c),
		GrantSource:    "admin",
	})
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	grant := grantFromProto(resp.GetGrant())
	h.auditLog(c, "grant-resource-group", "resource_grants", grant.GrantID, "success", fmt.Sprintf("target_user_id=%d", grant.TargetUserID))
	response.Created(c, grant)
}

func (h *Handler) ListResourceGrants(c *gin.Context) {
	options := shared.ListOptions(c)
	targetUserID, ok := optionalInt64Query(c, "target_user_id", "targetUserId")
	if !ok {
		return
	}
	resp, err := h.wallet.ListResourceGrants(c.Request.Context(), &walletv1.ListResourceGrantsRequest{
		RequestId:    middleware.CurrentRequestID(c),
		AppCode:      appctx.FromContext(c.Request.Context()),
		TargetUserId: targetUserID,
		Status:       options.Status,
		Page:         int32(options.Page),
		PageSize:     int32(options.PageSize),
	})
	if err != nil {
		response.ServerError(c, "获取资源赠送记录失败")
		return
	}
	items := make([]grantDTO, 0, len(resp.GetGrants()))
	for _, item := range resp.GetGrants() {
		items = append(items, grantFromProto(item))
	}
	if err := h.enrichGrantOperators(c.Request.Context(), items); err != nil {
		response.ServerError(c, "获取资源赠送操作人失败")
		return
	}
	response.OK(c, response.Page{Items: items, Page: options.Page, PageSize: options.PageSize, Total: resp.GetTotal()})
}

func (h *Handler) setResourceStatus(c *gin.Context, status string) {
	resourceID, ok := parseID(c, "resource_id")
	if !ok {
		return
	}
	resp, err := h.wallet.SetResourceStatus(c.Request.Context(), &walletv1.SetResourceStatusRequest{
		RequestId:      middleware.CurrentRequestID(c),
		AppCode:        appctx.FromContext(c.Request.Context()),
		ResourceId:     resourceID,
		Status:         status,
		OperatorUserId: actorID(c),
	})
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	resource := resourceFromProto(resp.GetResource())
	h.auditLog(c, "set-resource-status", "resources", fmt.Sprintf("%d", resource.ResourceID), "success", status)
	response.OK(c, resource)
}

func (h *Handler) setResourceGroupStatus(c *gin.Context, status string) {
	groupID, ok := parseID(c, "group_id")
	if !ok {
		return
	}
	resp, err := h.wallet.SetResourceGroupStatus(c.Request.Context(), &walletv1.SetResourceGroupStatusRequest{
		RequestId:      middleware.CurrentRequestID(c),
		AppCode:        appctx.FromContext(c.Request.Context()),
		GroupId:        groupID,
		Status:         status,
		OperatorUserId: actorID(c),
	})
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	group := resourceGroupFromProto(resp.GetGroup())
	h.auditLog(c, "set-resource-group-status", "resource_groups", fmt.Sprintf("%d", group.GroupID), "success", status)
	response.OK(c, group)
}

func (h *Handler) setGiftStatus(c *gin.Context, status string) {
	giftID := strings.TrimSpace(c.Param("gift_id"))
	if giftID == "" {
		response.BadRequest(c, "ID 参数不正确")
		return
	}
	resp, err := h.wallet.SetGiftConfigStatus(c.Request.Context(), &walletv1.SetGiftConfigStatusRequest{
		RequestId:      middleware.CurrentRequestID(c),
		AppCode:        appctx.FromContext(c.Request.Context()),
		GiftId:         giftID,
		Status:         status,
		OperatorUserId: actorID(c),
	})
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	gift := giftFromProto(resp.GetGift())
	h.auditLog(c, "set-gift-status", "gift_configs", gift.GiftID, "success", status)
	response.OK(c, gift)
}

func (h *Handler) auditLog(c *gin.Context, action string, resource string, resourceID string, status string, detail string) {
	shared.OperationLogWithResourceID(c, h.audit, action, resource, resourceID, status, detail)
}

func actorID(c *gin.Context) int64 {
	return int64(shared.ActorFromContext(c).UserID)
}

func parseID(c *gin.Context, name string) (int64, bool) {
	raw := strings.TrimSpace(c.Param(name))
	value, err := strconv.ParseInt(raw, 10, 64)
	if err != nil || value <= 0 {
		response.BadRequest(c, "ID 参数不正确")
		return 0, false
	}
	return value, true
}

func optionalInt64Query(c *gin.Context, primary string, fallback string) (int64, bool) {
	value, _, ok := optionalInt64QueryWithPresence(c, primary, fallback)
	return value, ok
}

func optionalInt64QueryWithPresence(c *gin.Context, primary string, fallback string) (int64, bool, bool) {
	raw := strings.TrimSpace(c.Query(primary))
	if raw == "" {
		raw = strings.TrimSpace(c.Query(fallback))
	}
	if raw == "" {
		return 0, false, true
	}
	value, err := strconv.ParseInt(raw, 10, 64)
	if err != nil || value < 0 {
		response.BadRequest(c, primary+" 参数不正确")
		return 0, false, false
	}
	return value, true, true
}
