package availability

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

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

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

	server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
		require.Equal(t, http.MethodPut, request.Method)
		require.Equal(t, "/internal/maas/v1/hosted-entitlements/grants/ohg_1", request.URL.Path)
		require.Equal(t, "Bearer test-token", request.Header.Get("Authorization"))
		require.Equal(t, "application/json", request.Header.Get("Content-Type"))

		var payload EntitlementGrantRequest
		require.NoError(t, json.NewDecoder(request.Body).Decode(&payload))
		require.Equal(t, "org_123", payload.OrganizationID)
		require.Equal(t, "glm_5_1", payload.HostedModelID)
		require.Equal(t, "agentteam_eligible_org_sync", payload.GrantSource)
		require.Equal(t, "active", payload.Status)

		writer.Header().Set("Content-Type", "application/json")
		_, _ = writer.Write([]byte(`{
			"object": "maas.hosted_entitlement_grant",
			"id": "ohg_1",
			"organizationId": "org_123",
			"hostedModelId": "glm_5_1",
			"grantSource": "agentteam_eligible_org_sync",
			"status": "active",
			"createdAt": "2026-06-15T00:00:00Z",
			"updatedAt": "2026-06-15T00:00:00Z"
		}`))
	}))
	t.Cleanup(server.Close)

	client := NewClient(server.URL, "test-token", server.Client())
	grant, err := client.UpsertHostedEntitlementGrant(t.Context(), " ohg_1 ", EntitlementGrantRequest{
		OrganizationID: "org_123",
		HostedModelID:  "glm_5_1",
		GrantSource:    "agentteam_eligible_org_sync",
		Status:         "active",
	})

	require.NoError(t, err)
	require.Equal(t, "ohg_1", grant.ID)
	require.Equal(t, "org_123", grant.OrganizationID)
	require.Equal(t, "glm_5_1", grant.HostedModelID)
}

func TestLoadHostedCatalogUsesDeploymentCatalogEndpoint(t *testing.T) {
	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/hosted-catalog", request.URL.Path)
		require.Empty(t, request.URL.Query().Get("organization_id"))
		require.Equal(t, "Bearer test-token", request.Header.Get("Authorization"))
		writer.Header().Set("Content-Type", "application/json")
		_, _ = writer.Write([]byte(`{
			"object": "maas.hosted_catalog",
			"updatedAt": "2026-07-25T00:00:00Z",
			"models": [{
				"hostedModelId": "model_a",
				"modelReference": "agentteam/model-a",
				"displayName": "Model A",
				"inventoryStatus": "active",
				"runtimeStatus": "supported",
				"enabled": true,
				"executableWireApiFamilies": ["chat_completions"],
				"chatCompletionsFeatures": ["chat_completions.reasoning_effort", "chat_completions.tools"],
				"chatCompletionsCompatibilityProfile": {
					"id": "chat_completions_deepseek_v4_thinking_v1",
					"supported_tool_choice_modes": ["auto"],
					"tool_choice_parameter_accepted": false,
					"parallel_tool_calls_parameter_accepted": false,
					"assistant_tool_call_reasoning_replay": "required",
					"assistant_tool_call_content": "non_null_string",
					"output_token_field": "max_tokens",
					"supports_store_false": false,
					"supports_n_one": false,
					"reasoning_effort_map": {"low":"high","medium":"high","high":"high","xhigh":"max","max":"max"}
				}
			}]
		}`))
	}))
	defer server.Close()

	catalog, err := NewClient(server.URL, "test-token", server.Client()).LoadHostedCatalog(t.Context())
	require.NoError(t, err)
	require.Equal(t, "maas.hosted_catalog", catalog.Object)
	require.Equal(t, "agentteam/model-a", catalog.Models[0].ModelReference)
	require.True(t, catalog.Models[0].Enabled)
	levels, defaultLevel, ok := catalog.Models[0].ChatCompletionsCompatibilityProfile.KnowledgeQAReasonLevels()
	require.True(t, ok)
	require.Equal(t, []string{"high"}, levels)
	require.Equal(t, "high", defaultLevel)
}

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

	first := HostedEntitlementGrantID(" org_123 ", "hosted_model_id", "glm_5_1")
	second := HostedEntitlementGrantID("org_123", " hosted_model_id ", " glm_5_1 ")

	require.Equal(t, first, second)
	require.Len(t, first, len("ohg_agentteam_auto_")+32)
}

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

	var availability Availability
	require.NoError(t, json.Unmarshal([]byte(`{
		"object": "maas.hosted_availability",
		"organizationId": "org_123",
		"models": [{
			"hostedModelId": "glm_5_1",
			"modelReference": "agentteam/glm-5.1",
			"displayName": "GLM 5.1",
			"enabled": true,
			"accessSource": "default_open",
			"supportedFamilies": ["chat_completions", "responses"],
			"executableWireApiFamilies": ["responses"],
			"messagesFeatures": ["tool_use"],
			"chatCompletionsFeatures": ["json_object"],
			"responsesFeatures": ["text_format"]
		}]
	}`), &availability))

	require.Len(t, availability.Models, 1)
	model := availability.Models[0]
	require.Equal(t, "default_open", model.AccessSource)
	require.Equal(t, []string{"chat_completions", "responses"}, model.SupportedFamilies)
	require.Equal(t, []string{"responses"}, model.ExecutableWireAPIFamilies)
	require.Equal(t, []string{"tool_use"}, model.MessagesFeatures)
	require.Equal(t, []string{"json_object"}, model.ChatCompletionsFeatures)
	require.Equal(t, []string{"text_format"}, model.ResponsesFeatures)
}

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

	var model Model
	require.NoError(t, json.Unmarshal([]byte(`{
		"hostedModelId": "glm_5_1",
		"modelReference": "agentteam/glm-5.1",
		"accessSource": "org_grant"
	}`), &model))

	require.Equal(t, "org_grant", model.AccessSource)
}

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

	profile, ok := knownChatCompletionsCompatibilityProfile(ChatCompletionsProfileOpenAIRequiredToolsV1)
	require.True(t, ok)
	levels, defaultLevel, supported := profile.KnowledgeQAReasonLevels()
	require.True(t, supported)
	require.Equal(t, []string{"low", "medium", "high"}, levels)
	require.Equal(t, "medium", defaultLevel)

	profile.OutputTokenField = "max_tokens"
	_, _, supported = profile.KnowledgeQAReasonLevels()
	require.False(t, supported)

	profile.ID = "chat_completions_future_v99"
	_, _, supported = profile.KnowledgeQAReasonLevels()
	require.False(t, supported)
}

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

	profile, ok := knownChatCompletionsCompatibilityProfile(ChatCompletionsProfileDeepSeekV4ThinkingV1)
	require.True(t, ok)
	raw, err := json.Marshal(profile)
	require.NoError(t, err)

	var object map[string]any
	require.NoError(t, json.Unmarshal(raw, &object))
	object["future_semantic"] = true
	withUnknownField, err := json.Marshal(object)
	require.NoError(t, err)
	var decoded ChatCompletionsCompatibilityProfile
	require.ErrorContains(t, json.Unmarshal(withUnknownField, &decoded), "unknown field")

	delete(object, "future_semantic")
	delete(object, "supports_n_one")
	withMissingFalseField, err := json.Marshal(object)
	require.NoError(t, err)
	require.ErrorContains(t, json.Unmarshal(withMissingFalseField, &decoded), "missing field")
}
