package financeorder

import (
	"errors"
	"strconv"
	"strings"

	"hyapp-admin-server/internal/integration/userclient"
	"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"

	"github.com/gin-gonic/gin"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"gorm.io/gorm"
)

type Handler struct {
	service *Service
	audit   shared.OperationLogger
}

func New(store *repository.Store, wallet walletclient.Client, user userclient.Client, audit shared.OperationLogger, options ...Option) *Handler {
	return &Handler{service: NewService(store, wallet, user, options...), audit: audit}
}

func (h *Handler) ListCoinSellerRechargeOrders(c *gin.Context) {
	options := shared.ListOptions(c)
	items, total, err := h.service.ListOrders(repository.CoinSellerRechargeOrderListOptions{
		Page:          options.Page,
		PageSize:      options.PageSize,
		AppCode:       firstQuery(c, "app_code", "appCode"),
		Status:        options.Status,
		VerifyStatus:  firstQuery(c, "verify_status", "verifyStatus"),
		GrantStatus:   firstQuery(c, "grant_status", "grantStatus"),
		ProviderCode:  firstQuery(c, "provider_code", "providerCode"),
		Keyword:       options.Keyword,
		TargetUserID:  parseInt64Query(c, "target_user_id", "targetUserId"),
		OperatorID:    uint(parseInt64Query(c, "operator_id", "operatorId")),
		CreatedFromMS: parseInt64Query(c, "created_from_ms", "createdFromMs", "start_ms", "startMs"),
		CreatedToMS:   parseInt64Query(c, "created_to_ms", "createdToMs", "end_ms", "endMs"),
	})
	if err != nil {
		writeServiceError(c, err, "获取币商充值订单失败")
		return
	}
	response.OK(c, response.Page{Items: items, Page: options.Page, PageSize: options.PageSize, Total: total})
}

func (h *Handler) GetCoinSellerRechargeOrder(c *gin.Context) {
	id, ok := shared.ParseID(c, "order_id")
	if !ok {
		return
	}
	item, err := h.service.GetOrder(id)
	if err != nil {
		writeServiceError(c, err, "获取币商充值订单失败")
		return
	}
	response.OK(c, item)
}

func (h *Handler) CreateCoinSellerRechargeOrder(c *gin.Context) {
	var req createCoinSellerRechargeOrderRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "币商充值订单参数不正确")
		return
	}
	item, err := h.service.CreateOrder(c.Request.Context(), shared.ActorFromContext(c), req, middleware.CurrentRequestID(c))
	if err != nil {
		writeServiceError(c, err, "创建币商充值订单失败")
		return
	}
	shared.OperationLogWithResourceID(c, h.audit, "create-coin-seller-recharge-order", "admin_coin_seller_recharge_orders", idString(item.ID), "success", item.ProviderCode+":"+item.ExternalOrderNo)
	response.Created(c, item)
}

func (h *Handler) QuoteCoinSellerRechargeOrder(c *gin.Context) {
	var req coinSellerRechargeQuoteRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "币商充值报价参数不正确")
		return
	}
	item, err := h.service.QuoteOrder(c.Request.Context(), shared.ActorFromContext(c), req, middleware.CurrentRequestID(c))
	if err != nil {
		writeServiceError(c, err, "生成币商充值报价失败")
		return
	}
	response.OK(c, item)
}

func (h *Handler) GetCoinSellerRechargeExchangeRate(c *gin.Context) {
	item, err := h.service.GetExchangeRatePolicy(c.Param("app_code"))
	if err != nil {
		writeServiceError(c, err, "获取币商充值汇率配置失败")
		return
	}
	response.OK(c, item)
}

func (h *Handler) GetUSDTAddresses(c *gin.Context) {
	item, err := h.service.GetUSDTAddresses(shared.ActorFromContext(c), c.Param("app_code"))
	if err != nil {
		writeServiceError(c, err, "获取 USDT 收款地址失败")
		return
	}
	response.OK(c, item)
}

func (h *Handler) UpsertUSDTAddress(c *gin.Context) {
	var req financeUSDTAddressUpsertRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "USDT 收款地址参数不正确")
		return
	}
	item, err := h.service.UpsertUSDTAddress(shared.ActorFromContext(c), c.Param("app_code"), c.Param("chain"), req)
	if err != nil {
		writeServiceError(c, err, "保存 USDT 收款地址失败")
		return
	}
	// 收款地址决定真实资金凭证归属，审计资源 ID 同时包含 App 与链，便于后续定位误改来源。
	shared.OperationLogWithResourceID(c, h.audit, "upsert-finance-usdt-address", "admin_app_configs", item.AppCode+":"+item.Chain, "success", "")
	response.OK(c, item)
}

func (h *Handler) ReplaceCoinSellerRechargeExchangeRate(c *gin.Context) {
	var req coinSellerRechargeExchangeRatePolicyRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "币商充值汇率配置参数不正确")
		return
	}
	item, err := h.service.ReplaceExchangeRatePolicy(shared.ActorFromContext(c), c.Param("app_code"), req)
	if err != nil {
		writeServiceError(c, err, "保存币商充值汇率配置失败")
		return
	}
	shared.OperationLogWithResourceID(c, h.audit, "replace-coin-seller-recharge-exchange-rate", "admin_app_configs", item.AppCode, "success", "tiers="+strconv.Itoa(len(item.Tiers))+",whitelist="+strconv.Itoa(len(item.Whitelist)))
	response.OK(c, item)
}

func (h *Handler) VerifyCoinSellerRechargeReceipt(c *gin.Context) {
	var req verifyCoinSellerRechargeReceiptRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "币商充值凭证参数不正确")
		return
	}
	item, err := h.service.VerifyReceipt(c.Request.Context(), shared.ActorFromContext(c), req, middleware.CurrentRequestID(c))
	if err != nil {
		writeServiceError(c, err, "校验币商充值凭证失败")
		return
	}
	shared.OperationLogWithResourceID(c, h.audit, "verify-coin-seller-recharge-receipt", "admin_coin_seller_recharge_orders", item.ExternalOrderNo, "success", item.ProviderCode+":"+item.Status)
	response.OK(c, item)
}

func (h *Handler) VerifyCoinSellerRechargeOrder(c *gin.Context) {
	id, ok := shared.ParseID(c, "order_id")
	if !ok {
		return
	}
	item, err := h.service.VerifyOrder(c.Request.Context(), shared.ActorFromContext(c), id, middleware.CurrentRequestID(c))
	if err != nil {
		writeServiceError(c, err, "校验币商充值订单失败")
		return
	}
	shared.OperationLogWithResourceID(c, h.audit, "verify-coin-seller-recharge-order", "admin_coin_seller_recharge_orders", idString(item.ID), "success", item.VerifyStatus)
	response.OK(c, item)
}

func (h *Handler) GrantCoinSellerRechargeOrder(c *gin.Context) {
	id, ok := shared.ParseID(c, "order_id")
	if !ok {
		return
	}
	item, err := h.service.GrantOrder(c.Request.Context(), shared.ActorFromContext(c), id, middleware.CurrentRequestID(c))
	if err != nil {
		writeServiceError(c, err, "发放币商充值金币失败")
		return
	}
	shared.OperationLogWithResourceID(c, h.audit, "grant-coin-seller-recharge-order", "admin_coin_seller_recharge_orders", idString(item.ID), "success", item.WalletTransactionID)
	response.OK(c, item)
}

func writeServiceError(c *gin.Context, err error, fallback string) {
	if errors.Is(err, gorm.ErrRecordNotFound) {
		response.NotFound(c, "币商充值订单不存在")
		return
	}
	if errors.Is(err, errCoinSellerRechargeForbidden) {
		response.Forbidden(c, err.Error())
		return
	}
	if errors.Is(err, repository.ErrCoinSellerRechargeOrderNotVerified) {
		response.BadRequest(c, "订单尚未校验通过")
		return
	}
	if errors.Is(err, repository.ErrCoinSellerRechargeOrderGranting) {
		response.BadRequest(c, "订单正在发放中")
		return
	}
	if errors.Is(err, repository.ErrCoinSellerRechargeOrderGranted) {
		response.BadRequest(c, "订单已发放")
		return
	}
	if errors.Is(err, errInvalidCoinSellerRechargeOrderInput) {
		response.BadRequest(c, err.Error())
		return
	}
	if errors.Is(err, errLegacyCoinSellerNotFound) {
		response.BadRequest(c, "目标用户不是启用中的币商/货运代理")
		return
	}
	if errors.Is(err, errLegacyCoinSellerOrderDuplicate) {
		response.BadRequest(c, "legacy 账套中订单号已入账")
		return
	}
	if isDuplicateKeyError(err) {
		response.BadRequest(c, "订单号已绑定，不能重复使用")
		return
	}
	if status.Code(err) == codes.NotFound {
		response.NotFound(c, err.Error())
		return
	}
	if st, ok := status.FromError(err); ok && strings.TrimSpace(st.Message()) != "" {
		switch st.Code() {
		case codes.PermissionDenied:
			response.Forbidden(c, st.Message())
			return
		case codes.InvalidArgument, codes.FailedPrecondition, codes.AlreadyExists, codes.Aborted, codes.ResourceExhausted:
			response.BadRequest(c, st.Message())
			return
		case codes.Unavailable:
			response.ServerError(c, fallback)
			return
		}
	}
	if strings.Contains(err.Error(), "not configured") {
		response.ServerError(c, fallback)
		return
	}
	response.BadRequest(c, err.Error())
}

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

func parseInt64Query(c *gin.Context, keys ...string) int64 {
	value := firstQuery(c, keys...)
	if value == "" {
		return 0
	}
	parsed, _ := strconv.ParseInt(value, 10, 64)
	return parsed
}

func idString(id uint) string {
	return strconv.FormatUint(uint64(id), 10)
}
