package payment

import (
	"bytes"
	"context"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"

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

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

func TestListThirdPartyPaymentChannelsForwardsIncludeDisabledMethods(t *testing.T) {
	wallet := &mockPaymentWallet{thirdPartyChannelsResp: &walletv1.ListThirdPartyPaymentChannelsResponse{
		Channels: []*walletv1.ThirdPartyPaymentChannel{{
			AppCode:      "lalu",
			ProviderCode: "mifapay",
			ProviderName: "MiFaPay",
			Status:       "active",
			SortOrder:    10,
			Methods: []*walletv1.ThirdPartyPaymentMethod{{
				MethodId:          810,
				AppCode:           "lalu",
				ProviderCode:      "mifapay",
				ProviderName:      "MiFaPay",
				CountryCode:       "SA",
				CountryName:       "Saudi Arabia",
				CurrencyCode:      "SAR",
				PayWay:            "Card",
				PayType:           "MADA",
				MethodName:        "MADA",
				Status:            "active",
				UsdToCurrencyRate: "1.00000000",
			}},
		}},
	}}
	router := newPaymentHandlerTestRouter(New(wallet, nil, nil))
	request := httptest.NewRequest(http.MethodGet, "/admin/payment/third-party-channels?provider_code=mifapay&status=active", nil)
	recorder := httptest.NewRecorder()

	router.ServeHTTP(recorder, request)

	if recorder.Code != http.StatusOK {
		t.Fatalf("status mismatch: got %d body=%s", recorder.Code, recorder.Body.String())
	}
	if wallet.lastThirdPartyChannels == nil ||
		wallet.lastThirdPartyChannels.GetAppCode() != "lalu" ||
		wallet.lastThirdPartyChannels.GetProviderCode() != "mifapay" ||
		wallet.lastThirdPartyChannels.GetStatus() != "active" ||
		!wallet.lastThirdPartyChannels.GetIncludeDisabledMethods() {
		t.Fatalf("third party channels request mismatch: %+v", wallet.lastThirdPartyChannels)
	}
	var response adminPaymentTestResponse
	if err := json.NewDecoder(recorder.Body).Decode(&response); err != nil {
		t.Fatalf("decode response failed: %v", err)
	}
	items := response.Data["items"].([]any)
	channel := items[0].(map[string]any)
	methods := channel["methods"].([]any)
	if response.Code != 0 || channel["providerCode"] != "mifapay" || len(methods) != 1 || methods[0].(map[string]any)["payType"] != "MADA" {
		t.Fatalf("third party channels response mismatch: %+v", response)
	}
}

func TestSetThirdPartyPaymentMethodStatusForwardsOperator(t *testing.T) {
	wallet := &mockPaymentWallet{}
	router := newPaymentHandlerTestRouter(New(wallet, nil, nil))
	request := httptest.NewRequest(http.MethodPatch, "/admin/payment/third-party-methods/810/status", bytes.NewBufferString(`{"enabled":false}`))
	request.Header.Set("Content-Type", "application/json")
	recorder := httptest.NewRecorder()

	router.ServeHTTP(recorder, request)

	if recorder.Code != http.StatusOK {
		t.Fatalf("status mismatch: got %d body=%s", recorder.Code, recorder.Body.String())
	}
	if wallet.lastSetMethodStatus == nil ||
		wallet.lastSetMethodStatus.GetMethodId() != 810 ||
		wallet.lastSetMethodStatus.GetEnabled() ||
		wallet.lastSetMethodStatus.GetOperatorUserId() != 7 {
		t.Fatalf("method status request mismatch: %+v", wallet.lastSetMethodStatus)
	}
}

func TestUpdateThirdPartyPaymentRateTrimsRateAndForwardsOperator(t *testing.T) {
	wallet := &mockPaymentWallet{}
	router := newPaymentHandlerTestRouter(New(wallet, nil, nil))
	request := httptest.NewRequest(http.MethodPatch, "/admin/payment/third-party-rates/810", bytes.NewBufferString(`{"usdToCurrencyRate":" 3.75000000 "}`))
	request.Header.Set("Content-Type", "application/json")
	recorder := httptest.NewRecorder()

	router.ServeHTTP(recorder, request)

	if recorder.Code != http.StatusOK {
		t.Fatalf("status mismatch: got %d body=%s", recorder.Code, recorder.Body.String())
	}
	if wallet.lastUpdateRate == nil ||
		wallet.lastUpdateRate.GetMethodId() != 810 ||
		wallet.lastUpdateRate.GetUsdToCurrencyRate() != "3.75000000" ||
		wallet.lastUpdateRate.GetOperatorUserId() != 7 {
		t.Fatalf("payment rate request mismatch: %+v", wallet.lastUpdateRate)
	}
}

func TestCreateRechargeProductForwardsWebCoinSellerAudience(t *testing.T) {
	wallet := &mockPaymentWallet{}
	router := newPaymentHandlerTestRouter(New(wallet, nil, nil))
	body := `{"amountUsdt":"10.000000","coinAmount":880000,"productName":"Seller 10 USD","description":"coin seller tier","audienceType":"coin_seller","platform":"web","regionIds":[7100],"enabled":true}`
	request := httptest.NewRequest(http.MethodPost, "/admin/payment/recharge-products", bytes.NewBufferString(body))
	request.Header.Set("Content-Type", "application/json")
	recorder := httptest.NewRecorder()

	router.ServeHTTP(recorder, request)

	if recorder.Code != http.StatusCreated {
		t.Fatalf("status mismatch: got %d body=%s", recorder.Code, recorder.Body.String())
	}
	if wallet.lastCreateProduct == nil ||
		wallet.lastCreateProduct.GetAmountMicro() != 10_000_000 ||
		wallet.lastCreateProduct.GetCoinAmount() != 880000 ||
		wallet.lastCreateProduct.GetAudienceType() != "coin_seller" ||
		wallet.lastCreateProduct.GetPlatform() != "web" ||
		len(wallet.lastCreateProduct.GetRegionIds()) != 1 ||
		wallet.lastCreateProduct.GetRegionIds()[0] != 7100 ||
		!wallet.lastCreateProduct.GetEnabled() ||
		wallet.lastCreateProduct.GetOperatorUserId() != 7 {
		t.Fatalf("create recharge product request mismatch: %+v", wallet.lastCreateProduct)
	}
}

func newPaymentHandlerTestRouter(handler *Handler) *gin.Engine {
	gin.SetMode(gin.TestMode)
	router := gin.New()
	router.Use(func(c *gin.Context) {
		// 后台 handler 只依赖 app_code、request_id 和操作者；测试在这里模拟鉴权中间件已完成后的上下文。
		c.Request = c.Request.WithContext(appctx.WithContext(c.Request.Context(), "lalu"))
		c.Set(middleware.ContextRequestID, "payment-handler-test")
		c.Set(middleware.ContextUserID, uint(7))
		c.Set(middleware.ContextUsername, "tester")
		c.Next()
	})
	router.GET("/admin/payment/third-party-channels", handler.ListThirdPartyPaymentChannels)
	router.PATCH("/admin/payment/third-party-methods/:method_id/status", handler.SetThirdPartyPaymentMethodStatus)
	router.PATCH("/admin/payment/third-party-rates/:method_id", handler.UpdateThirdPartyPaymentRate)
	router.POST("/admin/payment/recharge-products", handler.CreateRechargeProduct)
	return router
}

type adminPaymentTestResponse struct {
	Code int            `json:"code"`
	Data map[string]any `json:"data"`
}

type mockPaymentWallet struct {
	walletclient.Client

	lastThirdPartyChannels *walletv1.ListThirdPartyPaymentChannelsRequest
	thirdPartyChannelsResp *walletv1.ListThirdPartyPaymentChannelsResponse
	lastSetMethodStatus    *walletv1.SetThirdPartyPaymentMethodStatusRequest
	lastUpdateRate         *walletv1.UpdateThirdPartyPaymentRateRequest
	lastCreateProduct      *walletv1.CreateRechargeProductRequest
}

func (m *mockPaymentWallet) ListThirdPartyPaymentChannels(_ context.Context, req *walletv1.ListThirdPartyPaymentChannelsRequest) (*walletv1.ListThirdPartyPaymentChannelsResponse, error) {
	m.lastThirdPartyChannels = req
	if m.thirdPartyChannelsResp != nil {
		return m.thirdPartyChannelsResp, nil
	}
	return &walletv1.ListThirdPartyPaymentChannelsResponse{}, nil
}

func (m *mockPaymentWallet) SetThirdPartyPaymentMethodStatus(_ context.Context, req *walletv1.SetThirdPartyPaymentMethodStatusRequest) (*walletv1.ThirdPartyPaymentMethodResponse, error) {
	m.lastSetMethodStatus = req
	status := "disabled"
	if req.GetEnabled() {
		status = "active"
	}
	return &walletv1.ThirdPartyPaymentMethodResponse{Method: &walletv1.ThirdPartyPaymentMethod{
		MethodId:     req.GetMethodId(),
		AppCode:      req.GetAppCode(),
		ProviderCode: "mifapay",
		ProviderName: "MiFaPay",
		CountryCode:  "SA",
		PayWay:       "Card",
		PayType:      "MADA",
		Status:       status,
	}}, nil
}

func (m *mockPaymentWallet) UpdateThirdPartyPaymentRate(_ context.Context, req *walletv1.UpdateThirdPartyPaymentRateRequest) (*walletv1.ThirdPartyPaymentMethodResponse, error) {
	m.lastUpdateRate = req
	return &walletv1.ThirdPartyPaymentMethodResponse{Method: &walletv1.ThirdPartyPaymentMethod{
		MethodId:          req.GetMethodId(),
		AppCode:           req.GetAppCode(),
		ProviderCode:      "mifapay",
		ProviderName:      "MiFaPay",
		CountryCode:       "SA",
		PayWay:            "Card",
		PayType:           "MADA",
		Status:            "active",
		UsdToCurrencyRate: req.GetUsdToCurrencyRate(),
	}}, nil
}

func (m *mockPaymentWallet) CreateRechargeProduct(_ context.Context, req *walletv1.CreateRechargeProductRequest) (*walletv1.RechargeProductResponse, error) {
	m.lastCreateProduct = req
	return &walletv1.RechargeProductResponse{Product: &walletv1.RechargeProduct{
		AppCode:      req.GetAppCode(),
		ProductId:    9001,
		ProductCode:  "h5_seller_10",
		ProductName:  req.GetProductName(),
		Description:  req.GetDescription(),
		AudienceType: req.GetAudienceType(),
		Platform:     req.GetPlatform(),
		AmountMicro:  req.GetAmountMicro(),
		CoinAmount:   req.GetCoinAmount(),
		RegionIds:    append([]int64(nil), req.GetRegionIds()...),
		Enabled:      req.GetEnabled(),
		Status:       "active",
	}}, nil
}
