package resource

import (
	"context"
	"fmt"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
	"time"

	"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 TestUpdateMP4ResourceLayoutsPreservesResourceFields(t *testing.T) {
	wallet := &mockResourceWallet{resources: map[int64]*walletv1.Resource{
		11: {
			AppCode:             "lalu",
			ResourceId:          11,
			ResourceCode:        "profile_card_star",
			ResourceType:        resourceTypeProfileCard,
			Name:                "星光资料卡",
			Status:              "active",
			Grantable:           true,
			GrantStrategy:       "extend_expiry",
			WalletAssetType:     "RESOURCE",
			WalletAssetAmount:   1,
			UsageScopes:         []string{resourceTypeProfileCard},
			AssetUrl:            "https://cdn.example.test/profile_card.png",
			PreviewUrl:          "https://cdn.example.test/profile_card_cover.png",
			AnimationUrl:        "https://cdn.example.test/profile_card.mp4?token=1",
			MetadataJson:        `{"profile_card_layout":{"source_width":1136,"source_height":1680,"color_content_width":750,"content_top":117,"content_bottom":1666,"content_height":1550,"detect_version":1}}`,
			SortOrder:           9,
			ManagerGrantEnabled: true,
			PriceType:           resourcePriceTypeCoin,
			CoinPrice:           100,
		},
	}}
	router := newResourceHandlerTestRouter(New(wallet, nil, nil, time.Second, nil))
	metadataJSON := `{
		"animation_format":"mp4",
		"mp4_alpha_layout":{
			"alpha_layout":"alpha_left_rgb_right",
			"video_w":1500,
			"video_h":1334,
			"rgb_frame":[750,0,750,1334],
			"alpha_frame":[0,0,750,1334],
			"confirmed":true,
			"detect_version":1
		},
		"debug":true
	}`
	body := fmt.Sprintf(`{"items":[{"resourceId":11,"metadataJson":%q}]}`, metadataJSON)

	recorder := httptest.NewRecorder()
	request := httptest.NewRequest(http.MethodPut, "/admin/resources/mp4-layouts/batch", strings.NewReader(body))
	request.Header.Set("Content-Type", "application/json")
	router.ServeHTTP(recorder, request)

	if recorder.Code != http.StatusOK {
		t.Fatalf("batch mp4 layout update status mismatch: %d %s", recorder.Code, recorder.Body.String())
	}
	if len(wallet.updates) != 1 {
		t.Fatalf("expected one wallet update, got %d", len(wallet.updates))
	}
	update := wallet.updates[0]
	if update.GetResourceId() != 11 || update.GetName() != "星光资料卡" || update.GetStatus() != "active" || update.GetCoinPrice() != 100 {
		t.Fatalf("resource fields should be preserved: %+v", update)
	}
	if update.ManagerGrantEnabled == nil || !update.GetManagerGrantEnabled() || update.GetOperatorUserId() != 7 {
		t.Fatalf("operator and manager grant should be preserved: %+v", update)
	}
	if !strings.Contains(update.GetMetadataJson(), `"profile_card_layout"`) || !strings.Contains(update.GetMetadataJson(), `"mp4_alpha_layout"`) {
		t.Fatalf("profile card and mp4 metadata should both be kept: %s", update.GetMetadataJson())
	}
	if strings.Contains(update.GetMetadataJson(), "debug") {
		t.Fatalf("debug metadata should be dropped: %s", update.GetMetadataJson())
	}
}

func TestUpdateMP4ResourceLayoutsIgnoresNonMP4Resource(t *testing.T) {
	wallet := &mockResourceWallet{resources: map[int64]*walletv1.Resource{
		12: {
			AppCode:       "lalu",
			ResourceId:    12,
			ResourceCode:  "gift_rose",
			ResourceType:  "gift",
			Name:          "玫瑰",
			Status:        "active",
			AnimationUrl:  "https://cdn.example.test/gift.svga",
			MetadataJson:  "{}",
			GrantStrategy: "increase_quantity",
			PriceType:     resourcePriceTypeCoin,
			CoinPrice:     10,
		},
	}}
	router := newResourceHandlerTestRouter(New(wallet, nil, nil, time.Second, nil))
	body := fmt.Sprintf(`{"items":[{"resourceId":12,"metadataJson":%q}]}`, mp4AlphaLayoutMetadataJSON("alpha_left_rgb_right", true))

	recorder := httptest.NewRecorder()
	request := httptest.NewRequest(http.MethodPut, "/admin/resources/mp4-layouts/batch", strings.NewReader(body))
	request.Header.Set("Content-Type", "application/json")
	router.ServeHTTP(recorder, request)

	if recorder.Code != http.StatusOK {
		t.Fatalf("non-mp4 resource should be ignored, got %d %s", recorder.Code, recorder.Body.String())
	}
	if len(wallet.updates) != 0 {
		t.Fatalf("non-mp4 resource should not update wallet")
	}
}

func TestDeleteGiftOnlyCallsGiftConfigDelete(t *testing.T) {
	wallet := &mockResourceWallet{}
	router := newResourceHandlerTestRouter(New(wallet, nil, nil, time.Second, nil))

	recorder := httptest.NewRecorder()
	request := httptest.NewRequest(http.MethodDelete, "/admin/gifts/rose", nil)
	router.ServeHTTP(recorder, request)

	if recorder.Code != http.StatusOK {
		t.Fatalf("delete gift status mismatch: %d %s", recorder.Code, recorder.Body.String())
	}
	if len(wallet.deletedGifts) != 1 {
		t.Fatalf("expected one gift delete request, got %d", len(wallet.deletedGifts))
	}
	deleteReq := wallet.deletedGifts[0]
	if deleteReq.GetAppCode() != "lalu" || deleteReq.GetGiftId() != "rose" || deleteReq.GetOperatorUserId() != 7 {
		t.Fatalf("delete gift request mismatch: %+v", deleteReq)
	}
}

func newResourceHandlerTestRouter(handler *Handler) *gin.Engine {
	gin.SetMode(gin.TestMode)
	router := gin.New()
	router.Use(func(c *gin.Context) {
		c.Request = c.Request.WithContext(appctx.WithContext(c.Request.Context(), "lalu"))
		c.Set(middleware.ContextRequestID, "resource-handler-test")
		c.Set(middleware.ContextUserID, uint(7))
		c.Set(middleware.ContextUsername, "tester")
		c.Next()
	})
	router.PUT("/admin/resources/mp4-layouts/batch", handler.UpdateMP4ResourceLayouts)
	router.DELETE("/admin/gifts/:gift_id", handler.DeleteGift)
	return router
}

type mockResourceWallet struct {
	walletclient.Client
	resources    map[int64]*walletv1.Resource
	updates      []*walletv1.UpdateResourceRequest
	deletedGifts []*walletv1.DeleteGiftConfigRequest
}

func (m *mockResourceWallet) GetResource(ctx context.Context, req *walletv1.GetResourceRequest) (*walletv1.GetResourceResponse, error) {
	resource := m.resources[req.GetResourceId()]
	if resource == nil {
		return nil, fmt.Errorf("resource not found")
	}
	return &walletv1.GetResourceResponse{Resource: resource}, nil
}

func (m *mockResourceWallet) UpdateResource(ctx context.Context, req *walletv1.UpdateResourceRequest) (*walletv1.ResourceResponse, error) {
	m.updates = append(m.updates, req)
	return &walletv1.ResourceResponse{Resource: &walletv1.Resource{
		AppCode:             req.GetAppCode(),
		ResourceId:          req.GetResourceId(),
		ResourceCode:        req.GetResourceCode(),
		ResourceType:        req.GetResourceType(),
		Name:                req.GetName(),
		Status:              req.GetStatus(),
		Grantable:           req.GetGrantable(),
		GrantStrategy:       req.GetGrantStrategy(),
		WalletAssetType:     req.GetWalletAssetType(),
		WalletAssetAmount:   req.GetWalletAssetAmount(),
		UsageScopes:         req.GetUsageScopes(),
		AssetUrl:            req.GetAssetUrl(),
		PreviewUrl:          req.GetPreviewUrl(),
		AnimationUrl:        req.GetAnimationUrl(),
		MetadataJson:        req.GetMetadataJson(),
		SortOrder:           req.GetSortOrder(),
		ManagerGrantEnabled: req.GetManagerGrantEnabled(),
		PriceType:           req.GetPriceType(),
		CoinPrice:           req.GetCoinPrice(),
	}}, nil
}

func (m *mockResourceWallet) DeleteGiftConfig(ctx context.Context, req *walletv1.DeleteGiftConfigRequest) (*walletv1.GiftConfigResponse, error) {
	m.deletedGifts = append(m.deletedGifts, req)
	return &walletv1.GiftConfigResponse{Gift: &walletv1.GiftConfig{
		AppCode:    req.GetAppCode(),
		GiftId:     req.GetGiftId(),
		ResourceId: 11,
		Status:     "active",
		Name:       "Rose",
	}}, nil
}
