package callback

import (
	"context"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"strings"

	"github.com/gofiber/fiber/v2"
	"github.com/valyala/fasthttp/fasthttpadaptor"
	"go.uber.org/zap"

	"github.com/break/junhong_cmp_fiber/internal/model"
	orderService "github.com/break/junhong_cmp_fiber/internal/service/order"
	rechargeService "github.com/break/junhong_cmp_fiber/internal/service/recharge"
	rechargeOrderSvc "github.com/break/junhong_cmp_fiber/internal/service/recharge_order"
	"github.com/break/junhong_cmp_fiber/internal/store/postgres"
	"github.com/break/junhong_cmp_fiber/pkg/alipay"
	"github.com/break/junhong_cmp_fiber/pkg/constants"
	"github.com/break/junhong_cmp_fiber/pkg/errors"
	"github.com/break/junhong_cmp_fiber/pkg/fuiou"
	"github.com/break/junhong_cmp_fiber/pkg/wechat"
	"gorm.io/gorm"
)

// AgentRechargeServiceInterface 代理充值服务接口
type AgentRechargeServiceInterface interface {
	HandlePaymentCallback(ctx context.Context, rechargeNo string, paymentMethod string, paymentTransactionID string, paidAmount int64) error
}

// WechatConfigServiceInterface 支付配置服务接口
type WechatConfigServiceInterface interface {
	GetActiveConfig(ctx context.Context) (*model.WechatConfig, error)
	GetConfigForCallback(ctx context.Context, orderNo string) (*model.WechatConfig, error)
}

type PaymentHandler struct {
	orderService         *orderService.Service
	rechargeService      *rechargeService.Service
	rechargeOrderService *rechargeOrderSvc.Service
	agentRechargeService AgentRechargeServiceInterface
	wechatPayment        wechat.PaymentServiceInterface
	wechatConfigService  WechatConfigServiceInterface
	paymentStore         *postgres.PaymentStore
	logger               *zap.Logger
}

func NewPaymentHandler(
	orderService *orderService.Service,
	rechargeService *rechargeService.Service,
	rechargeOrderService *rechargeOrderSvc.Service,
	agentRechargeService AgentRechargeServiceInterface,
	wechatPayment wechat.PaymentServiceInterface,
	wechatConfigService WechatConfigServiceInterface,
	paymentStore *postgres.PaymentStore,
	logger *zap.Logger,
) *PaymentHandler {
	return &PaymentHandler{
		orderService:         orderService,
		rechargeService:      rechargeService,
		rechargeOrderService: rechargeOrderService,
		agentRechargeService: agentRechargeService,
		wechatPayment:        wechatPayment,
		wechatConfigService:  wechatConfigService,
		paymentStore:         paymentStore,
		logger:               logger,
	}
}

// WechatPayCallback 微信支付回调（带签名验证）
// POST /api/callback/wechat-pay
func (h *PaymentHandler) WechatPayCallback(c *fiber.Ctx) error {
	body := c.Body()
	ctx := context.Background()

	// 预解析订单号（不验签），用于按 payment_config_id 加载创建订单时所用的配置
	orderNo, err := wechat.PeekOrderNo(body)
	if err != nil {
		h.logger.Error("微信回调：预解析订单号失败", zap.Error(err))
		return errors.New(errors.CodeWechatCallbackInvalid, "回调数据格式错误")
	}

	cfg, err := h.wechatConfigService.GetConfigForCallback(ctx, orderNo)
	if err != nil || cfg == nil {
		h.logger.Error("微信回调：加载支付配置失败",
			zap.String("order_no", orderNo),
			zap.Error(err),
		)
		return errors.New(errors.CodeWechatCallbackInvalid, "微信支付服务未配置")
	}

	switch cfg.ProviderType {
	case model.ProviderTypeWechatV2:
		paymentSvc := wechat.NewPaymentV2Service(cfg.OaAppID, cfg.WxMchID, cfg.WxAPIV2Key, cfg.WxNotifyURL, h.logger)
		result, err := paymentSvc.VerifyCallback(body)
		if err != nil {
			return errors.Wrap(errors.CodeWechatCallbackInvalid, err, "微信 v2 回调验签失败")
		}
		if result.TradeState != "SUCCESS" {
			return h.wechatV2SuccessResponse(c)
		}
		// TotalFee 为字符串格式的分，解析失败则降级为 0（后续 handlePaymentCallback 会记录日志）
		totalFee, _ := strconv.ParseInt(result.TotalFee, 10, 64)
		if err := h.dispatchWechatCallback(ctx, result.OutTradeNo, result.TransactionID, totalFee); err != nil {
			return errors.Wrap(errors.CodeWechatCallbackInvalid, err, "处理微信支付回调失败")
		}

	case model.ProviderTypeWechat:
		if h.wechatPayment == nil {
			return errors.New(errors.CodeWechatCallbackInvalid, "微信 v3 支付未初始化")
		}
		var httpReq http.Request
		fasthttpadaptor.ConvertRequest(c.Context(), &httpReq, true)
		_, err := h.wechatPayment.HandlePaymentNotify(&httpReq, func(result *wechat.PaymentNotifyResult) error {
			if result.TradeState != "SUCCESS" {
				return nil
			}
			return h.dispatchWechatCallback(ctx, result.OutTradeNo, result.TransactionID, result.TotalAmount)
		})
		if err != nil {
			return errors.Wrap(errors.CodeWechatCallbackInvalid, err, "处理微信支付回调失败")
		}

	default:
		return errors.New(errors.CodeWechatCallbackInvalid, "不支持的支付渠道类型")
	}

	return h.wechatV2SuccessResponse(c)
}

func (h *PaymentHandler) dispatchPaymentRecordCallback(ctx context.Context, paymentNo, paymentMethod, transactionID string, paidAmount int64) (bool, error) {
	if h.paymentStore != nil {
		payment, err := h.paymentStore.GetByPaymentNo(ctx, paymentNo)
		if err == nil {
			switch payment.OrderType {
			case model.PaymentOrderTypePackage:
				return true, h.orderService.HandlePaymentRecordCallback(ctx, paymentNo, paymentMethod, transactionID, paidAmount)
			case model.PaymentOrderTypeRecharge:
				if h.rechargeOrderService != nil {
					return true, h.rechargeOrderService.HandlePaymentCallback(ctx, paymentNo, paymentMethod, transactionID)
				}
				return true, fmt.Errorf("充值订单服务未配置，无法处理支付单: %s", paymentNo)
			default:
				return true, fmt.Errorf("未知支付记录类型: %s", payment.OrderType)
			}
		}
		if err != gorm.ErrRecordNotFound {
			return true, errors.Wrap(errors.CodeDatabaseError, err, "查询支付记录失败")
		}
	}
	return false, nil
}

// dispatchWechatCallback 优先按支付单分发，旧单号继续按前缀兼容处理。
func (h *PaymentHandler) dispatchWechatCallback(ctx context.Context, outTradeNo, transactionID string, paidAmount int64) error {
	handled, err := h.dispatchPaymentRecordCallback(ctx, outTradeNo, model.PaymentMethodWechat, transactionID, paidAmount)
	if handled || err != nil {
		return err
	}

	switch {
	case strings.HasPrefix(outTradeNo, "ORD"):
		return h.orderService.HandlePaymentCallback(ctx, outTradeNo, model.PaymentMethodWechat, paidAmount)
	case strings.HasPrefix(outTradeNo, constants.AssetRechargeOrderPrefix):
		if h.rechargeOrderService != nil {
			return h.rechargeOrderService.HandlePaymentCallback(ctx, outTradeNo, model.PaymentByWechat, transactionID)
		}
		return fmt.Errorf("充值订单服务未配置，无法处理订单: %s", outTradeNo)
	case strings.HasPrefix(outTradeNo, constants.AgentRechargeOrderPrefix):
		if h.agentRechargeService != nil {
			return h.agentRechargeService.HandlePaymentCallback(ctx, outTradeNo, model.PaymentMethodWechat, transactionID, paidAmount)
		}
		return fmt.Errorf("代理充值服务未配置，无法处理订单: %s", outTradeNo)
	default:
		return fmt.Errorf("未知订单号前缀: %s", outTradeNo)
	}
}

// wechatV2SuccessResponse 返回微信 v2 要求的 XML 格式成功响应
// v2 API 要求 XML 格式的 return_code=SUCCESS，否则微信会持续重试回调
func (h *PaymentHandler) wechatV2SuccessResponse(c *fiber.Ctx) error {
	c.Set("Content-Type", "text/xml; charset=utf-8")
	return c.SendString(`<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>`)
}

// AlipayCallback 支付宝异步回调（带验签与多维度安全校验）
// POST /api/callback/alipay
func (h *PaymentHandler) AlipayCallback(c *fiber.Ctx) error {
	ctx := c.UserContext()

	// 从原始请求体解析 form 参数（x-www-form-urlencoded）
	values, err := url.ParseQuery(string(c.Body()))
	if err != nil {
		h.logger.Error("支付宝回调：解析 form 参数失败", zap.Error(err))
		return errors.New(errors.CodeInvalidParam, "回调数据格式错误")
	}

	// 未验签前只读取 out_trade_no，用于回溯创建支付单时所用的配置
	outTradeNo := values.Get("out_trade_no")
	if outTradeNo == "" {
		h.logger.Error("支付宝回调：out_trade_no 为空")
		return errors.New(errors.CodeInvalidParam, "订单号不能为空")
	}

	// 按 payment_config_id 加载创建支付单时所用的配置（支持已停用配置）
	cfg, err := h.wechatConfigService.GetConfigForCallback(ctx, outTradeNo)
	if err != nil || cfg == nil {
		h.logger.Error("支付宝回调：加载支付配置失败",
			zap.String("out_trade_no", outTradeNo),
			zap.Error(err),
		)
		return errors.New(errors.CodeWechatCallbackInvalid, "支付配置不可用")
	}

	// 使用支付宝公钥验签（DecodeNotification 内部完成签名校验）
	notification, err := alipay.DecodeNotification(ctx, cfg, values)
	if err != nil {
		h.logger.Error("支付宝回调：验签失败",
			zap.String("out_trade_no", outTradeNo),
			zap.Uint("config_id", cfg.ID),
			zap.Error(err),
		)
		return errors.New(errors.CodeWechatCallbackInvalid, "支付宝回调验签失败")
	}

	// 校验 app_id 与配置一致，防止跨商户混用
	if notification.AppId != cfg.AliAppID {
		h.logger.Error("支付宝回调：app_id 不匹配",
			zap.String("out_trade_no", outTradeNo),
			zap.String("notify_app_id", notification.AppId),
			zap.String("config_app_id", cfg.AliAppID),
		)
		return errors.New(errors.CodeWechatCallbackInvalid, "支付宝 app_id 校验失败")
	}

	// 非终态交易（如 WAIT_BUYER_PAY、TRADE_CLOSED）直接返回 success，不推进业务
	tradeStatus := string(notification.TradeStatus)
	if tradeStatus != "TRADE_SUCCESS" && tradeStatus != "TRADE_FINISHED" {
		h.logger.Info("支付宝回调：非成功交易状态，忽略",
			zap.String("out_trade_no", outTradeNo),
			zap.String("trade_status", tradeStatus),
		)
		return c.SendString("success")
	}

	// 查询支付记录
	payment, err := h.paymentStore.GetByPaymentNo(ctx, outTradeNo)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			h.logger.Error("支付宝回调：支付记录不存在",
				zap.String("out_trade_no", outTradeNo),
			)
			return errors.New(errors.CodeWechatCallbackInvalid, "支付记录不存在")
		}
		h.logger.Error("支付宝回调：查询支付记录失败",
			zap.String("out_trade_no", outTradeNo),
			zap.Error(err),
		)
		return errors.Wrap(errors.CodeDatabaseError, err, "查询支付记录失败")
	}

	// 校验支付方式必须为 alipay，防止其他通道的 payment_no 被误用
	if payment.PaymentMethod != model.PaymentByAlipay {
		h.logger.Error("支付宝回调：支付方式不匹配",
			zap.String("out_trade_no", outTradeNo),
			zap.String("payment_method", payment.PaymentMethod),
		)
		return errors.New(errors.CodeWechatCallbackInvalid, "支付通道校验失败")
	}

	// 校验 payment_config_id 与当前配置一致；旧数据为空时兼容并记录 warn
	if payment.PaymentConfigID == nil {
		h.logger.Warn("支付宝回调：payment_config_id 为空，跳过配置 ID 校验（旧数据兼容）",
			zap.String("out_trade_no", outTradeNo),
			zap.Uint("config_id", cfg.ID),
		)
	} else if *payment.PaymentConfigID != cfg.ID {
		h.logger.Error("支付宝回调：payment_config_id 不匹配",
			zap.String("out_trade_no", outTradeNo),
			zap.Uint("payment_config_id", *payment.PaymentConfigID),
			zap.Uint("callback_config_id", cfg.ID),
		)
		return errors.New(errors.CodeWechatCallbackInvalid, "支付配置 ID 校验失败")
	}

	// 校验金额：使用 total_amount（原始订单金额）而非 buyer_pay_amount，
	// 避免优惠券/积分场景导致内部订单金额被错误记低
	notifyAmountFen, err := alipay.YuanToFen(notification.TotalAmount)
	if err != nil {
		h.logger.Error("支付宝回调：total_amount 转换失败",
			zap.String("out_trade_no", outTradeNo),
			zap.String("total_amount", notification.TotalAmount),
			zap.Error(err),
		)
		return errors.New(errors.CodeWechatCallbackInvalid, "支付金额格式错误")
	}
	if notifyAmountFen != payment.Amount {
		h.logger.Error("支付宝回调：金额不一致",
			zap.String("out_trade_no", outTradeNo),
			zap.Int64("notify_amount_fen", notifyAmountFen),
			zap.Int64("payment_amount_fen", payment.Amount),
		)
		return errors.New(errors.CodeWechatCallbackInvalid, "支付金额校验失败")
	}

	// 写入支付宝交易流水号
	if err := h.paymentStore.UpdatePaymentInfo(ctx, payment.ID, notification.TradeNo, nil); err != nil {
		h.logger.Error("支付宝回调：写入 third_party_trade_no 失败",
			zap.String("out_trade_no", outTradeNo),
			zap.String("trade_no", notification.TradeNo),
			zap.Error(err),
		)
		// 不中断业务，继续分发（幂等业务层会处理状态）
	}

	// 按支付单 order_type 分发业务
	switch payment.OrderType {
	case model.PaymentOrderTypePackage:
		if err := h.orderService.HandlePaymentRecordCallback(ctx, outTradeNo, model.PaymentByAlipay, notification.TradeNo, payment.Amount); err != nil {
			h.logger.Error("支付宝回调：推进套餐订单失败",
				zap.String("out_trade_no", outTradeNo),
				zap.String("trade_no", notification.TradeNo),
				zap.Error(err),
			)
			return errors.Wrap(errors.CodeInternalError, err, "处理支付宝支付回调失败")
		}
		h.logger.Info("支付宝回调：套餐订单支付成功",
			zap.String("out_trade_no", outTradeNo),
			zap.String("trade_no", notification.TradeNo),
			zap.String("order_type", payment.OrderType),
		)

	case model.PaymentOrderTypeRecharge:
		if h.rechargeOrderService == nil {
			h.logger.Error("支付宝回调：充值订单服务未配置",
				zap.String("out_trade_no", outTradeNo),
			)
			return errors.New(errors.CodeInternalError, "充值订单服务未配置")
		}
		if err := h.rechargeOrderService.HandlePaymentCallback(ctx, outTradeNo, model.PaymentByAlipay, notification.TradeNo); err != nil {
			h.logger.Error("支付宝回调：推进充值订单失败",
				zap.String("out_trade_no", outTradeNo),
				zap.String("trade_no", notification.TradeNo),
				zap.Error(err),
			)
			return errors.Wrap(errors.CodeInternalError, err, "处理支付宝充值回调失败")
		}
		h.logger.Info("支付宝回调：充值订单支付成功",
			zap.String("out_trade_no", outTradeNo),
			zap.String("trade_no", notification.TradeNo),
			zap.String("order_type", payment.OrderType),
		)

	default:
		h.logger.Error("支付宝回调：未知支付记录类型",
			zap.String("out_trade_no", outTradeNo),
			zap.String("order_type", payment.OrderType),
		)
		return errors.New(errors.CodeInternalError, "未知支付记录类型")
	}

	return c.SendString("success")
}

// FuiouPayCallback 富友支付回调（带签名验证）
// POST /api/callback/fuiou-pay
func (h *PaymentHandler) FuiouPayCallback(c *fiber.Ctx) error {
	body, bodySource := fuiouCallbackPayload(c)
	if strings.TrimSpace(string(body)) == "" {
		return errors.New(errors.CodeFuiouCallbackInvalid, "回调请求体为空")
	}

	ctx := c.UserContext()
	c.Set("Content-Type", "text/plain; charset=utf-8")

	// 预解析订单号（不验签），用于按 payment_config_id 加载创建订单时所用的配置
	preNotify, err := fuiou.ParseNotify(body)
	if err != nil {
		h.logger.Error("富友回调：预解析失败",
			zap.Error(err),
			zap.String("payload_source", bodySource),
			zap.Int("raw_body_len", len(c.Body())),
			zap.Int("payload_len", len(body)),
			zap.String("content_type", c.Get("Content-Type")),
			zap.String("query", string(c.Request().URI().QueryString())),
		)
		return c.Send(fuiou.BuildNotifyFailResponse("parse failed"))
	}

	cfg, err := h.wechatConfigService.GetConfigForCallback(ctx, preNotify.MchntOrderNo)
	if err != nil || cfg == nil {
		h.logger.Error("富友回调：加载支付配置失败",
			zap.String("order_no", preNotify.MchntOrderNo),
			zap.Error(err),
		)
		return c.Send(fuiou.BuildNotifyFailResponse("payment config unavailable"))
	}
	if cfg.ProviderType != model.ProviderTypeFuiou ||
		strings.TrimSpace(preNotify.InsCd) != strings.TrimSpace(cfg.FyInsCd) ||
		strings.TrimSpace(preNotify.MchntCd) != strings.TrimSpace(cfg.FyMchntCd) {
		h.logger.Error("富友回调：支付配置与回调商户不匹配",
			zap.String("order_no", preNotify.MchntOrderNo),
			zap.Uint("config_id", cfg.ID),
			zap.String("config_name", cfg.Name),
			zap.String("callback_ins_cd", preNotify.InsCd),
			zap.String("config_ins_cd", cfg.FyInsCd),
			zap.String("callback_mchnt_cd", preNotify.MchntCd),
			zap.String("config_mchnt_cd", cfg.FyMchntCd),
			zap.String("provider_type", cfg.ProviderType),
		)
		return c.Send(fuiou.BuildNotifyFailResponse("payment config mismatch"))
	}

	client, err := fuiou.NewClient(
		cfg.FyInsCd,
		cfg.FyMchntCd,
		cfg.FyTermID,
		cfg.FyAPIURL,
		cfg.FyNotifyURL,
		cfg.FyPrivateKey,
		cfg.FyPublicKey,
		h.logger,
	)
	if err != nil {
		h.logger.Error("富友回调：构造客户端失败", zap.Error(err))
		return c.Send(fuiou.BuildNotifyFailResponse("client init failed"))
	}

	notify, err := client.VerifyNotify(body)
	if err != nil {
		if notify != nil {
			h.logger.Warn("富友回调：非成功结果",
				zap.String("result_code", notify.ResultCode),
				zap.String("result_msg", notify.ResultMsg))
			return c.Send(fuiou.BuildNotifySuccessResponse())
		}
		h.logger.Error("富友回调：验签或解析失败",
			zap.Error(err),
			zap.String("order_no", preNotify.MchntOrderNo),
			zap.Uint("config_id", cfg.ID),
			zap.String("config_name", cfg.Name),
			zap.String("callback_ins_cd", preNotify.InsCd),
			zap.String("config_ins_cd", cfg.FyInsCd),
			zap.String("callback_mchnt_cd", preNotify.MchntCd),
			zap.String("config_mchnt_cd", cfg.FyMchntCd),
		)
		return c.Send(fuiou.BuildNotifyFailResponse(err.Error()))
	}

	orderNo := notify.MchntOrderNo
	// OrderAmt 为字符串格式的分，解析失败则降级为 0
	orderAmt, _ := strconv.ParseInt(notify.OrderAmt, 10, 64)
	if handled, err := h.dispatchPaymentRecordCallback(ctx, orderNo, "fuiou", notify.TransactionId, orderAmt); err != nil {
		return c.Send(fuiou.BuildNotifyFailResponse(err.Error()))
	} else if handled {
		return c.Send(fuiou.BuildNotifySuccessResponse())
	}

	switch {
	case strings.HasPrefix(orderNo, "ORD"):
		if err := h.orderService.HandlePaymentCallback(ctx, orderNo, "fuiou", orderAmt); err != nil {
			return c.Send(fuiou.BuildNotifyFailResponse(err.Error()))
		}
	case strings.HasPrefix(orderNo, constants.AssetRechargeOrderPrefix):
		if h.rechargeOrderService != nil {
			if err := h.rechargeOrderService.HandlePaymentCallback(ctx, orderNo, "fuiou", notify.TransactionId); err != nil {
				return c.Send(fuiou.BuildNotifyFailResponse(err.Error()))
			}
			return c.Send(fuiou.BuildNotifySuccessResponse())
		}
		return c.Send(fuiou.BuildNotifyFailResponse("充值订单服务未配置"))
	case strings.HasPrefix(orderNo, constants.AgentRechargeOrderPrefix):
		if h.agentRechargeService != nil {
			if err := h.agentRechargeService.HandlePaymentCallback(ctx, orderNo, model.ProviderTypeFuiou, notify.TransactionId, orderAmt); err != nil {
				return c.Send(fuiou.BuildNotifyFailResponse(err.Error()))
			}
		}
	default:
		return c.Send(fuiou.BuildNotifyFailResponse("unknown order prefix"))
	}

	return c.Send(fuiou.BuildNotifySuccessResponse())
}

// fuiouCallbackPayload 提取富友回调载荷，兼容 body、form req 和 query req 三种来源。
func fuiouCallbackPayload(c *fiber.Ctx) ([]byte, string) {
	if req := strings.TrimSpace(c.FormValue("req")); req != "" {
		return []byte(req), "form:req"
	}
	if req := strings.TrimSpace(c.Query("req")); req != "" {
		return []byte(req), "query:req"
	}
	return c.Body(), "body"
}
