package billing

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

	"github.com/stretchr/testify/require"
)

func TestMaaSClientCreatesCreditRechargeOrder(t *testing.T) {
	t.Parallel()

	expiresAt := time.Date(2026, 6, 3, 9, 30, 0, 0, time.UTC)
	server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
		require.Equal(t, http.MethodPost, request.Method)
		require.Equal(t, "/internal/maas/v1/billing/credit-recharge-orders", request.URL.Path)
		require.Equal(t, "Bearer internal-token", request.Header.Get("Authorization"))
		require.Equal(t, "application/json", request.Header.Get("Content-Type"))

		var payload map[string]any
		require.NoError(t, json.NewDecoder(request.Body).Decode(&payload))
		require.Equal(t, "org_1", payload["organizationId"])
		require.Equal(t, "wsp_1", payload["workspaceId"])
		require.Equal(t, "idem_1", payload["idempotencyKey"])
		require.Equal(t, float64(3000), payload["creditAmount"])
		priceSnapshot, ok := payload["priceSnapshot"].(map[string]any)
		require.True(t, ok)
		require.Equal(t, "hosted-credit-atoms-v1", priceSnapshot["source"])
		require.Equal(t, float64(1_000_000), priceSnapshot["creditAtomsPerCredit"])
		require.Equal(t, float64(100_000_000), priceSnapshot["creditAtomsPerCny"])
		writer.Header().Set("Content-Type", "application/json")
		_, _ = writer.Write([]byte(`{
			"orderId":"pay_1",
			"organizationId":"org_1",
			"workspaceId":"wsp_1",
			"createdByUserId":"usr_1",
			"outTradeNo":"agentteam_1",
			"status":"pending",
			"creditUnit":"agentteam_credit",
			"creditAmount":3000,
			"currency":"CNY",
			"payableAmountCents":3000,
			"provider":"alipay",
			"providerMode":"page_pay",
			"expiresAt":"2026-06-03T09:30:00Z",
			"createdAt":"2026-06-03T09:00:00Z",
			"updatedAt":"2026-06-03T09:00:00Z"
		}`))
	}))
	defer server.Close()

	client := NewMaaSClient(server.URL, "internal-token", server.Client())
	order, err := client.CreateCreditRechargeOrder(context.Background(), createCreditRechargeOrderParams{
		OrganizationID:     "org_1",
		WorkspaceID:        "wsp_1",
		CreatedByUserID:    "usr_1",
		IdempotencyKey:     "idem_1",
		CreditUnit:         billingCreditUnitAgentTeamCredit,
		CreditAmount:       3000,
		Currency:           billingCurrencyCNY,
		PayableAmountCents: 3000,
		PriceSnapshot: map[string]any{
			"source":               "hosted-credit-atoms-v1",
			"creditAtomsPerCredit": billingCreditAtomsPerCredit,
			"creditAtomsPerCny":    billingCreditAtomsPerCNY,
		},
		Provider:     billingProviderAlipay,
		ProviderMode: billingProviderModePagePay,
		ExpiresAt:    expiresAt,
	})

	require.NoError(t, err)
	require.Equal(t, "pay_1", order.OrderID)
	require.Equal(t, "agentteam_1", order.OutTradeNo)
	require.Equal(t, expiresAt, order.ExpiresAt)
}

func TestMaaSClientLoadsHostedPricingPublicReferences(t *testing.T) {
	t.Parallel()

	server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
		require.Equal(t, http.MethodGet, request.Method)
		require.Equal(t, "/internal/maas/v1/billing/pricing", request.URL.Path)
		require.Equal(t, "org_1", request.URL.Query().Get("organization_id"))
		require.Equal(t, "Bearer internal-token", request.Header.Get("Authorization"))

		writer.Header().Set("Content-Type", "application/json")
		_, _ = writer.Write([]byte(`{
			"creditUnit":"agentteam_credit",
			"currency":"CNY",
			"creditAmountPerCny":100,
			"creditAtomsPerCredit":1000000,
			"creditAtomsPerCny":100000000,
			"rechargePresets":[3000,5000,10000],
			"customMinCredits":100,
			"hostedModels":[{
				"id":"agentteam/deepseek-v4-flash",
				"modelReference":"agentteam/deepseek-v4-flash",
				"hostedModelId":"deepseek_v4_flash",
				"displayName":"DeepSeek V4 Flash",
				"inputCreditAtomsPerMillionTokens":2000000,
				"cacheReadInputCreditAtomsPerMillionTokens":200000,
				"outputCreditAtomsPerMillionTokens":8000000
			},{
				"id":"agentteam/context-tiered",
				"modelReference":"agentteam/context-tiered",
				"hostedModelId":"context_tiered",
				"displayName":"Context Tiered",
				"pricingVersionId":"hpv_context_discount_90",
				"effectivePricingVersionId":"hpv_context_discount_90",
				"pricingFormulaVersion":"hosted_token_context_threshold_v1",
				"billableDimensions":["input_tokens","cache_read_input_tokens","output_tokens"],
				"basePricingVersionId":"hpv_context_standard",
				"basePricingFormulaVersion":"hosted_token_context_threshold_v1",
				"baseBillableDimensions":["input_tokens","cache_read_input_tokens","output_tokens"],
				"contextTiers":[{
					"id":"short_context",
					"minContextTokens":0,
					"maxContextTokens":4096,
					"rates":{
						"inputTokensCreditAtomsPerMillionTokens":1000000,
						"cacheReadInputCreditAtomsPerMillionTokens":100000,
						"outputCreditAtomsPerMillionTokens":3000000
					}
				},{
					"id":"long_context",
					"minContextTokens":4097,
					"rates":{
						"inputTokensCreditAtomsPerMillionTokens":2000000,
						"cacheReadInputCreditAtomsPerMillionTokens":200000,
						"outputCreditAtomsPerMillionTokens":6000000
					}
				}],
				"baseContextTiers":[{
					"id":"short_context",
					"minContextTokens":0,
					"maxContextTokens":4096,
					"rates":{
						"inputTokensCreditAtomsPerMillionTokens":2000000,
						"cacheReadInputCreditAtomsPerMillionTokens":200000,
						"outputCreditAtomsPerMillionTokens":6000000
					}
				},{
					"id":"long_context",
					"minContextTokens":4097,
					"rates":{
						"inputTokensCreditAtomsPerMillionTokens":4000000,
						"cacheReadInputCreditAtomsPerMillionTokens":400000,
						"outputCreditAtomsPerMillionTokens":12000000
					}
				}],
				"pricingOverride":{
					"id":"hpod_context_campaign",
					"hostedModelId":"context_tiered",
					"scopeType":"global",
					"basePricingVersionId":"hpv_context_standard",
					"effectivePricingVersionId":"hpv_context_discount_90",
					"overrideType":"campaign",
					"displayLabel":"90% campaign",
					"priority":100,
					"status":"active"
				}
			}]
		}`))
	}))
	defer server.Close()

	client := NewMaaSClient(server.URL, "internal-token", server.Client())
	pricing, err := client.LoadPricing(context.Background(), "org_1")

	require.NoError(t, err)
	require.Equal(t, billingCreditUnitAgentTeamCredit, pricing.CreditUnit)
	require.Len(t, pricing.HostedModels, 2)
	model := pricing.HostedModels[0]
	require.Equal(t, "agentteam/deepseek-v4-flash", model.ID)
	require.Equal(t, "agentteam/deepseek-v4-flash", model.ModelReference)
	require.Equal(t, "deepseek_v4_flash", model.HostedModelID)
	require.NotNil(t, model.OutputCreditAtomsPerMillionTokens)
	require.Equal(t, int64(8_000_000), *model.OutputCreditAtomsPerMillionTokens)
	tieredModel := pricing.HostedModels[1]
	require.Nil(t, tieredModel.InputCreditAtomsPerMillionTokens)
	require.Equal(t, "hosted_token_context_threshold_v1", tieredModel.PricingFormulaVersion)
	require.Equal(t, []string{"input_tokens", "cache_read_input_tokens", "output_tokens"}, tieredModel.BillableDimensions)
	require.Len(t, tieredModel.ContextTiers, 2)
	require.NotNil(t, tieredModel.ContextTiers[0].MaxContextTokens)
	require.Equal(t, int64(4096), *tieredModel.ContextTiers[0].MaxContextTokens)
	require.Nil(t, tieredModel.ContextTiers[1].MaxContextTokens)
	require.Equal(t, int64(6_000_000), tieredModel.ContextTiers[1].Rates.OutputCreditAtomsPerMillionTokens)
	require.Equal(t, "hpv_context_discount_90", tieredModel.EffectivePricingVersionID)
	require.Equal(t, "hpv_context_standard", tieredModel.BasePricingVersionID)
	require.Len(t, tieredModel.BaseContextTiers, 2)
	require.Equal(t, int64(12_000_000), tieredModel.BaseContextTiers[1].Rates.OutputCreditAtomsPerMillionTokens)
	require.NotNil(t, tieredModel.PricingOverride)
	require.Equal(t, "90% campaign", tieredModel.PricingOverride.DisplayLabel)

	hostedPricing, err := client.LoadHostedPricing(context.Background(), "org_1")
	require.NoError(t, err)
	require.Equal(t, int64(1_000_000), hostedPricing.CreditAtomsPerCredit)
	require.Equal(t, "agentteam/deepseek-v4-flash", hostedPricing.HostedModels[0].ModelReference)
	require.Equal(t, "agentteam/context-tiered", hostedPricing.HostedModels[1].ModelReference)
	require.Len(t, hostedPricing.HostedModels[1].ContextTiers, 2)
	require.Len(t, hostedPricing.HostedModels[1].BaseContextTiers, 2)
	require.NotNil(t, hostedPricing.HostedModels[1].PricingOverride)
	require.Equal(t, "hpv_context_standard", hostedPricing.HostedModels[1].PricingOverride.BasePricingVersionID)
}

func TestMaaSClientGrantsHostedCredit(t *testing.T) {
	t.Parallel()

	expiresAt := time.Date(2026, 7, 1, 0, 0, 0, 0, time.UTC)
	createdAt := time.Date(2026, 6, 10, 8, 0, 0, 0, time.UTC)
	server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
		require.Equal(t, http.MethodPost, request.Method)
		require.Equal(t, "/internal/maas/v1/billing/credit-grants", request.URL.Path)
		require.Equal(t, "Bearer internal-token", request.Header.Get("Authorization"))
		require.Equal(t, "application/json", request.Header.Get("Content-Type"))

		var payload map[string]any
		require.NoError(t, json.NewDecoder(request.Body).Decode(&payload))
		require.Equal(t, "org_1", payload["organizationId"])
		require.Equal(t, "grant-idem", payload["idempotencyKey"])
		require.Equal(t, "agentteam_credit", payload["creditUnit"])
		require.Equal(t, float64(3000), payload["creditAmount"])
		require.Equal(t, "operator_gift", payload["grantSourceType"])
		require.Equal(t, "support-ticket-1", payload["grantReasonRef"])
		require.Equal(t, "usr_admin", payload["operatorIdentity"])
		require.Equal(t, "ticket-1", payload["sourceRef"])
		require.Equal(t, "2026-07-01T00:00:00Z", payload["expiresAt"])

		writer.Header().Set("Content-Type", "application/json")
		_, _ = writer.Write([]byte(`{
			"grantId":"hcg_1",
			"operationId":"hgo_1",
			"created":true,
			"organizationId":"org_1",
			"creditUnit":"agentteam_credit",
			"creditUnitSource":"credit_atoms",
			"creditAmount":3000,
			"creditAmountAtoms":3000000000,
			"grantSourceType":"operator_gift",
			"grantReasonRef":"support-ticket-1",
			"refundable":false,
			"expiresAt":"2026-07-01T00:00:00Z",
			"operatorIdentity":"usr_admin",
			"sourceRef":"ticket-1",
			"createdAt":"2026-06-10T08:00:00Z",
			"updatedAt":"2026-06-10T08:00:00Z"
		}`))
	}))
	defer server.Close()

	client := NewMaaSClient(server.URL, "internal-token", server.Client())
	grant, err := client.GrantHostedCredit(context.Background(), HostedCreditGrantParams{
		OrganizationID:   "org_1",
		IdempotencyKey:   "grant-idem",
		CreditUnit:       billingCreditUnitAgentTeamCredit,
		CreditAmount:     3000,
		GrantSourceType:  "operator_gift",
		GrantReasonRef:   "support-ticket-1",
		ExpiresAt:        &expiresAt,
		OperatorIdentity: "usr_admin",
		SourceRef:        "ticket-1",
	})

	require.NoError(t, err)
	require.True(t, grant.Created)
	require.Equal(t, "hcg_1", grant.GrantID)
	require.Equal(t, "hgo_1", grant.OperationID)
	require.Equal(t, int64(3_000_000_000), grant.CreditAmountAtoms)
	require.Equal(t, expiresAt, *grant.ExpiresAt)
	require.Equal(t, createdAt, grant.CreatedAt)
}

func TestMaaSClientReturnsStatusErrors(t *testing.T) {
	t.Parallel()

	server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
		writer.WriteHeader(http.StatusConflict)
		_, _ = writer.Write([]byte(`{"error":{"code":"billing.conflict"}}`))
	}))
	defer server.Close()

	client := NewMaaSClient(server.URL, "internal-token", server.Client())
	_, err := client.LoadSummary(context.Background(), "org_1")

	var maasErr MaaSClientError
	require.ErrorAs(t, err, &maasErr)
	require.Equal(t, http.StatusConflict, maasErr.StatusCode)
}
