package service

import (
	"context"
	"errors"
	paymentbiz "kra/internal/biz/payment"

	"kra/internal/service/dto"
)

type PaymentService struct{ uc *paymentbiz.PaymentUsecase }

func NewPaymentService(uc *paymentbiz.PaymentUsecase) *PaymentService { return &PaymentService{uc: uc} }
func (s *PaymentService) Order(ctx context.Context, provider, tradeNo string) (*dto.PaymentOrderResponse, error) {
	order, err := s.uc.Order(ctx, provider, tradeNo)
	if err != nil {
		return nil, err
	}
	return &dto.PaymentOrderResponse{
		ID:       order.ID,
		Provider: order.Provider, TradeNo: order.TradeNo, ProviderTradeNo: order.ProviderTradeNo,
		BusinessType: order.BusinessType, BusinessID: order.BusinessID, Subject: order.Subject,
		PaymentMode: order.PaymentMode, OriginalAmount: order.OriginalAmount,
		Amount: order.Amount, PaidAmount: order.PaidAmount, PayerPaidAmount: order.PayerPaidAmount,
		CashPaidAmount: order.CashPaidAmount, PointPaidAmount: order.PointPaidAmount,
		DiscountAmount: order.DiscountAmount, ProviderDiscountAmount: order.ProviderDiscountAmount,
		MerchantDiscountAmount: order.MerchantDiscountAmount, SettlementAmount: order.SettlementAmount,
		Currency: order.Currency, PayerCurrency: order.PayerCurrency, AmountBreakdownKnown: order.AmountBreakdownKnown,
		PaymentStatus: order.PaymentStatus, ProviderStatus: order.ProviderStatus, FulfillmentStatus: order.FulfillmentStatus,
		RefundStatus: order.RefundStatus, RefundedAmount: order.RefundedAmount, RefundRequestedAmount: order.RefundRequestedAmount, RefundNo: order.RefundNo, LastError: order.LastError,
		CreatedAt: order.CreatedAt, UpdatedAt: order.UpdatedAt, PaidAt: order.PaidAt,
		FulfilledAt: order.FulfilledAt, RefundedAt: order.RefundedAt,
	}, nil
}

func (s *PaymentService) Orders(ctx context.Context, req *dto.PaymentOrderListRequest) ([]*dto.PaymentOrderResponse, int64, int, int, error) {
	if req == nil {
		return nil, 0, 1, 10, errors.New("支付订单列表请求为空")
	}
	page, pageSize := req.Page, req.PageSize
	if page <= 0 {
		page = 1
	}
	if pageSize <= 0 {
		pageSize = 10
	}
	items, total, err := s.uc.Orders(ctx, page, pageSize, paymentbiz.PaymentOrderFilter{
		Provider: req.Provider, TradeNo: req.TradeNo, BusinessType: req.BusinessType,
		BusinessID: req.BusinessID, PaymentStatus: req.PaymentStatus, RefundStatus: req.RefundStatus,
	})
	if err != nil {
		return nil, 0, page, pageSize, err
	}
	result := make([]*dto.PaymentOrderResponse, 0, len(items))
	for _, item := range items {
		converted, convertErr := paymentOrderResponse(item)
		if convertErr != nil {
			return nil, 0, page, pageSize, convertErr
		}
		result = append(result, converted)
	}
	return result, total, page, pageSize, nil
}

func paymentOrderResponse(order *paymentbiz.PaymentOrder) (*dto.PaymentOrderResponse, error) {
	if order == nil {
		return nil, errors.New("支付订单为空")
	}
	return &dto.PaymentOrderResponse{
		ID: order.ID, Provider: order.Provider, TradeNo: order.TradeNo, ProviderTradeNo: order.ProviderTradeNo,
		BusinessType: order.BusinessType, BusinessID: order.BusinessID, Subject: order.Subject,
		PaymentMode: order.PaymentMode, OriginalAmount: order.OriginalAmount, Amount: order.Amount, PaidAmount: order.PaidAmount,
		PayerPaidAmount: order.PayerPaidAmount, CashPaidAmount: order.CashPaidAmount, PointPaidAmount: order.PointPaidAmount,
		DiscountAmount: order.DiscountAmount, ProviderDiscountAmount: order.ProviderDiscountAmount,
		MerchantDiscountAmount: order.MerchantDiscountAmount, SettlementAmount: order.SettlementAmount,
		Currency: order.Currency, PayerCurrency: order.PayerCurrency, AmountBreakdownKnown: order.AmountBreakdownKnown,
		PaymentStatus: order.PaymentStatus, ProviderStatus: order.ProviderStatus, FulfillmentStatus: order.FulfillmentStatus,
		RefundStatus: order.RefundStatus, RefundedAmount: order.RefundedAmount, RefundRequestedAmount: order.RefundRequestedAmount,
		RefundNo: order.RefundNo, LastError: order.LastError, CreatedAt: order.CreatedAt, UpdatedAt: order.UpdatedAt,
		PaidAt: order.PaidAt, FulfilledAt: order.FulfilledAt, RefundedAt: order.RefundedAt,
	}, nil
}
func (s *PaymentService) Create(ctx context.Context, req *dto.PaymentRequest) (*dto.PaymentResultResponse, error) {
	if req == nil {
		return nil, errors.New("支付请求为空")
	}
	result, err := s.uc.Create(ctx, &paymentbiz.PaymentRequest{
		Provider: req.Provider, TradeNo: req.TradeNo, Subject: req.Subject,
		Amount: req.Amount, OriginalAmount: req.OriginalAmount, Currency: req.Currency,
		NotifyURL: req.NotifyURL, ReturnURL: req.ReturnURL, ClientIP: req.ClientIP,
		Extra: req.Extra, BusinessType: req.BusinessType, BusinessID: req.BusinessID,
		PaymentMode: req.PaymentMode,
	})
	return paymentResultResponse(result), err
}
func (s *PaymentService) Query(ctx context.Context, req *dto.PaymentQueryRequest) (*dto.PaymentResultResponse, error) {
	if req == nil {
		return nil, errors.New("支付查询请求为空")
	}
	result, err := s.uc.Query(ctx, req.Provider, req.TradeNo)
	return paymentResultResponse(result), err
}
func (s *PaymentService) Refund(ctx context.Context, req *dto.PaymentRefundRequest) (*dto.PaymentResultResponse, error) {
	if req == nil {
		return nil, errors.New("支付退款请求为空")
	}
	result, err := s.uc.Refund(ctx, req.Provider, req.TradeNo, req.Amount)
	return paymentResultResponse(result), err
}

func (s *PaymentService) TestProvider(ctx context.Context, provider string) (*dto.PaymentTestResult, error) {
	result, err := s.uc.TestProvider(ctx, provider)
	if result == nil {
		return nil, err
	}
	response := &dto.PaymentTestResult{Provider: result.Provider, TradeNo: result.TradeNo, Passed: result.Passed, FullFlow: result.FullFlow, Mode: result.Mode, Stages: make([]*dto.PaymentTestStage, 0, len(result.Stages))}
	for _, stage := range result.Stages {
		response.Stages = append(response.Stages, &dto.PaymentTestStage{Name: stage.Name, Status: stage.Status, Message: stage.Message, TradeNo: stage.TradeNo, Duration: stage.Duration})
	}
	response.Result = paymentResultResponse(result.Result)
	return response, err
}

func (s *PaymentService) Fulfill(ctx context.Context, req *dto.PaymentFulfillRequest) (*dto.PaymentResultResponse, error) {
	if req == nil {
		return nil, errors.New("支付发货请求为空")
	}
	result, err := s.uc.Fulfill(ctx, req.Provider, req.TradeNo)
	return paymentResultResponse(result), err
}
func (s *PaymentService) Callback(ctx context.Context, req *dto.PaymentCallbackRequest) (dto.PaymentCallbackAck, error) {
	if req == nil {
		return paymentCallbackAckResponse(paymentbiz.DefaultPaymentCallbackAck("", false)), errors.New("支付回调请求为空")
	}
	result, err := s.uc.Callback(ctx, &paymentbiz.PaymentCallback{Provider: req.Provider, Headers: req.Headers, Body: req.Body, Query: req.Query})
	if err != nil {
		return paymentCallbackAckResponse(paymentbiz.CallbackFailure(err, paymentbiz.DefaultPaymentCallbackAck(req.Provider, false))), err
	}
	ack := paymentbiz.DefaultPaymentCallbackAck(req.Provider, true)
	if result != nil && result.SuccessAck.StatusCode != 0 {
		ack = result.SuccessAck
	}
	return paymentCallbackAckResponse(ack), nil
}

func DefaultPaymentCallbackAck(provider string, success bool) dto.PaymentCallbackAck {
	return paymentCallbackAckResponse(paymentbiz.DefaultPaymentCallbackAck(provider, success))
}

func paymentResultResponse(result *paymentbiz.PaymentResult) *dto.PaymentResultResponse {
	if result == nil {
		return nil
	}
	return &dto.PaymentResultResponse{
		Provider: result.Provider, Status: result.Status, TradeNo: result.TradeNo,
		ProviderTradeNo: result.ProviderTradeNo, Amount: result.Amount,
		PayerPaidAmount: result.PayerPaidAmount, CashPaidAmount: result.CashPaidAmount,
		PointPaidAmount: result.PointPaidAmount, DiscountAmount: result.DiscountAmount,
		ProviderDiscountAmount: result.ProviderDiscountAmount,
		MerchantDiscountAmount: result.MerchantDiscountAmount, SettlementAmount: result.SettlementAmount,
		Currency: result.Currency, PayerCurrency: result.PayerCurrency,
		AmountBreakdownKnown: result.AmountBreakdownKnown, Duplicate: result.Duplicate,
		Payload: result.Payload, OrderStatus: result.OrderStatus,
		FulfillmentStatus: result.FulfillmentStatus, RefundStatus: result.RefundStatus,
	}
}

func paymentCallbackAckResponse(ack paymentbiz.PaymentCallbackAck) dto.PaymentCallbackAck {
	return dto.PaymentCallbackAck{StatusCode: ack.StatusCode, ContentType: ack.ContentType, Body: ack.Body}
}
