package http

import (
	"bytes"
	"encoding/json"
	"hyapp/services/gateway-service/internal/transport/http/httpkit"
	"net/http"
	"net/http/httptest"
	"testing"

	userv1 "hyapp.local/api/proto/user/v1"
	walletv1 "hyapp.local/api/proto/wallet/v1"
	"hyapp/services/gateway-service/internal/auth"
)

func newManagerCenterTestRouter(walletClient *fakeWalletClient, hostClient *fakeUserHostClient, profileClient *fakeUserProfileClient) http.Handler {
	handler := NewHandlerWithClients(&fakeRoomClient{}, nil, nil, profileClient)
	handler.SetWalletClient(walletClient)
	handler.SetUserHostClient(hostClient)
	return handler.Routes(auth.NewVerifier("secret"))
}

func TestListManagerGrantResourcesChecksCapabilityAndFiltersWalletRequest(t *testing.T) {
	walletClient := &fakeWalletClient{
		listResourcesResp: &walletv1.ListResourcesResponse{
			Total: 1,
			Resources: []*walletv1.Resource{
				{
					ResourceId:        101,
					ResourceType:      "badge",
					Name:              "VIP Badge",
					AssetUrl:          "https://cdn.example/badge.png",
					PreviewUrl:        "https://cdn.example/badge-preview.png",
					WalletAssetType:   "",
					WalletAssetAmount: 0,
					SortOrder:         7,
				},
			},
		},
	}
	hostClient := &fakeUserHostClient{}
	router := newManagerCenterTestRouter(walletClient, hostClient, &fakeUserProfileClient{})
	request := httptest.NewRequest(http.MethodGet, "/api/v1/manager-center/resource-grants/resources?resource_type=badge&page=2&page_size=99", nil)
	request.Header.Set("Authorization", "Bearer "+signGatewayToken(t, "secret", 42))
	request.Header.Set("X-Request-ID", "req-manager-resources")
	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 hostClient.lastCapability == nil || hostClient.lastCapability.GetActorUserId() != 42 || hostClient.lastCapability.GetCapability() != managerResourceGrantCapability {
		t.Fatalf("manager capability request mismatch: %+v", hostClient.lastCapability)
	}
	if walletClient.lastListResources == nil {
		t.Fatal("wallet ListResources was not called")
	}
	generatedRequestID := assertGeneratedRequestID(t, recorder, "req-manager-resources")
	if req := walletClient.lastListResources; req.GetRequestId() != generatedRequestID || req.GetAppCode() != "lalu" || req.GetResourceType() != "badge" || req.GetPage() != 2 || req.GetPageSize() != 20 || !req.GetActiveOnly() || !req.GetManagerGrantOnly() {
		t.Fatalf("wallet ListResources request mismatch: %+v", req)
	}

	var envelope struct {
		Code      string `json:"code"`
		RequestID string `json:"request_id"`
		Data      struct {
			Items    []managerGrantResourceData `json:"items"`
			Total    int64                      `json:"total"`
			Page     int32                      `json:"page"`
			PageSize int32                      `json:"page_size"`
		} `json:"data"`
	}
	if err := json.NewDecoder(recorder.Body).Decode(&envelope); err != nil {
		t.Fatalf("decode response failed: %v", err)
	}
	if envelope.Code != httpkit.CodeOK || envelope.RequestID != generatedRequestID || envelope.Data.Total != 1 || len(envelope.Data.Items) != 1 {
		t.Fatalf("response envelope mismatch: %+v", envelope)
	}
	if envelope.Data.Items[0].ResourceID != "101" || envelope.Data.Items[0].Name != "VIP Badge" {
		t.Fatalf("resource data mismatch: %+v", envelope.Data.Items[0])
	}
}

func TestListManagerGrantResourcesRejectsUnauthorizedManager(t *testing.T) {
	walletClient := &fakeWalletClient{}
	hostClient := &fakeUserHostClient{capabilityResp: &userv1.CheckBusinessCapabilityResponse{Allowed: false, Reason: "active_agency_owner_required"}}
	router := newManagerCenterTestRouter(walletClient, hostClient, &fakeUserProfileClient{})
	request := httptest.NewRequest(http.MethodGet, "/api/v1/manager-center/resource-grants/resources", nil)
	request.Header.Set("Authorization", "Bearer "+signGatewayToken(t, "secret", 42))
	request.Header.Set("X-Request-ID", "req-manager-denied")
	recorder := httptest.NewRecorder()

	router.ServeHTTP(recorder, request)

	assertEnvelope(t, recorder, http.StatusForbidden, httpkit.CodePermissionDenied, "req-manager-denied")
	if walletClient.lastListResources != nil {
		t.Fatalf("unauthorized manager must not reach wallet-service: %+v", walletClient.lastListResources)
	}
}

func TestBusinessUserLookupForwardsSceneAndActor(t *testing.T) {
	profileClient := &fakeUserProfileClient{
		businessLookupResp: &userv1.BusinessUserLookupResponse{
			Users: []*userv1.BusinessUserLookupItem{
				{UserId: 900001, DisplayUserId: "123456", Username: "Alice", Avatar: "https://cdn.example/a.png", Status: userv1.UserStatus_USER_STATUS_ACTIVE, RegionId: 1001, RegionCode: "SA", RegionName: "Saudi Arabia"},
			},
		},
	}
	router := newManagerCenterTestRouter(&fakeWalletClient{}, &fakeUserHostClient{}, profileClient)
	request := httptest.NewRequest(http.MethodGet, "/api/v1/business/users/lookup?scene=manager_resource_grant&keyword=123456&page_size=99", nil)
	request.Header.Set("Authorization", "Bearer "+signGatewayToken(t, "secret", 42))
	request.Header.Set("X-Request-ID", "req-business-lookup")
	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 req := profileClient.lastBusinessLookup; req == nil || req.GetMeta().GetRequestId() != assertGeneratedRequestID(t, recorder, "req-business-lookup") || req.GetActorUserId() != 42 || req.GetScene() != "manager_resource_grant" || req.GetKeyword() != "123456" || req.GetPageSize() != 20 {
		t.Fatalf("business lookup request mismatch: %+v", req)
	}
	var envelope struct {
		Code string `json:"code"`
		Data struct {
			Items []businessUserLookupData `json:"items"`
			Total int                      `json:"total"`
		} `json:"data"`
	}
	if err := json.NewDecoder(recorder.Body).Decode(&envelope); err != nil {
		t.Fatalf("decode response failed: %v", err)
	}
	if envelope.Code != httpkit.CodeOK || envelope.Data.Total != 1 || envelope.Data.Items[0].UserID != "900001" || envelope.Data.Items[0].Status != "active" {
		t.Fatalf("business lookup response mismatch: %+v", envelope)
	}
}

func TestGrantManagerResourceUsesServerControlledGrantShape(t *testing.T) {
	walletClient := &fakeWalletClient{}
	hostClient := &fakeUserHostClient{}
	profileClient := &fakeUserProfileClient{}
	router := newManagerCenterTestRouter(walletClient, hostClient, profileClient)
	body := []byte(`{"command_id":"mgr-grant-1","target_user_id":"900001","resource_id":"101"}`)
	request := httptest.NewRequest(http.MethodPost, "/api/v1/manager-center/resource-grants", bytes.NewReader(body))
	request.Header.Set("Authorization", "Bearer "+signGatewayToken(t, "secret", 42))
	request.Header.Set("X-Request-ID", "req-manager-grant")
	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 hostClient.lastCapability == nil || hostClient.lastCapability.GetActorUserId() != 42 {
		t.Fatalf("manager capability request mismatch: %+v", hostClient.lastCapability)
	}
	if profileClient.lastGet == nil || profileClient.lastGet.GetUserId() != 900001 {
		t.Fatalf("target user lookup mismatch: %+v", profileClient.lastGet)
	}
	if req := walletClient.lastGrantResource; req == nil || req.GetCommandId() != "mgr-grant-1" || req.GetTargetUserId() != 900001 || req.GetResourceId() != 101 || req.GetQuantity() != 1 || req.GetDurationMs() != 0 || req.GetOperatorUserId() != 42 || req.GetGrantSource() != "manager_center" || req.GetReason() != "manager_center" {
		t.Fatalf("wallet GrantResource request mismatch: %+v", req)
	}
}

func TestGrantManagerResourceRejectsClientComputedQuantityOrDuration(t *testing.T) {
	walletClient := &fakeWalletClient{}
	router := newManagerCenterTestRouter(walletClient, &fakeUserHostClient{}, &fakeUserProfileClient{})
	body := []byte(`{"command_id":"mgr-grant-1","target_user_id":"900001","resource_id":"101","quantity":9}`)
	request := httptest.NewRequest(http.MethodPost, "/api/v1/manager-center/resource-grants", bytes.NewReader(body))
	request.Header.Set("Authorization", "Bearer "+signGatewayToken(t, "secret", 42))
	request.Header.Set("X-Request-ID", "req-manager-grant-shape")
	recorder := httptest.NewRecorder()

	router.ServeHTTP(recorder, request)

	assertEnvelope(t, recorder, http.StatusBadRequest, httpkit.CodeInvalidArgument, "req-manager-grant-shape")
	if walletClient.lastGrantResource != nil {
		t.Fatalf("client computed grant shape must not reach wallet-service: %+v", walletClient.lastGrantResource)
	}
}
