package server

import (
	"net/http"
	"testing"

	grpcruntime "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestMakeGatewayMux_MarshalerSelection(t *testing.T) {
	s := &HttpServer{}
	mux := s.makeGatewayMux(grpcruntime.DefaultHTTPErrorHandler)

	tests := map[string]struct {
		acceptHeader      string
		contentType       string
		wantOutboundSnake bool // true = snake_case (UseProtoNames: true)
		wantInboundSnake  bool
	}{
		"no headers - defaults to snake case": {
			wantOutboundSnake: true,
			wantInboundSnake:  true,
		},
		"accept application/json - snake case": {
			acceptHeader:      "application/json",
			wantOutboundSnake: true,
			wantInboundSnake:  true,
		},
		"accept application/json+camel - camel case outbound": {
			acceptHeader:      "application/json+camel",
			wantOutboundSnake: false,
			wantInboundSnake:  true,
		},
		"accept wildcard - snake case": {
			acceptHeader:      "*/*",
			wantOutboundSnake: true,
			wantInboundSnake:  true,
		},
		"content-type application/json - snake case inbound": {
			contentType:       "application/json",
			wantOutboundSnake: true,
			wantInboundSnake:  true,
		},
		"content-type application/json+camel - camel case inbound": {
			contentType:       "application/json+camel",
			wantOutboundSnake: false, // outbound falls back to inbound when no Accept
			wantInboundSnake:  false,
		},
		"accept snake + content-type camel - mixed": {
			acceptHeader:      "application/json",
			contentType:       "application/json+camel",
			wantOutboundSnake: true,
			wantInboundSnake:  false,
		},
	}

	for name, tc := range tests {
		t.Run(name, func(t *testing.T) {
			req, err := http.NewRequest("GET", "/test", nil)
			require.NoError(t, err)

			if tc.acceptHeader != "" {
				req.Header.Set("Accept", tc.acceptHeader)
			}
			if tc.contentType != "" {
				req.Header.Set("Content-Type", tc.contentType)
			}

			inbound, outbound := grpcruntime.MarshalerForRequest(mux, req)

			outboundJSONPb, ok := outbound.(*grpcruntime.JSONPb)
			require.True(t, ok, "expected outbound to be *grpcruntime.JSONPb")
			assert.Equal(t, tc.wantOutboundSnake, outboundJSONPb.MarshalOptions.UseProtoNames,
				"outbound UseProtoNames mismatch")

			inboundJSONPb, ok := inbound.(*grpcruntime.JSONPb)
			require.True(t, ok, "expected inbound to be *grpcruntime.JSONPb")
			assert.Equal(t, tc.wantInboundSnake, inboundJSONPb.MarshalOptions.UseProtoNames,
				"inbound UseProtoNames mismatch")
		})
	}
}

func TestMakeGatewayMux_EmitUnpopulated(t *testing.T) {
	s := &HttpServer{}
	mux := s.makeGatewayMux(grpcruntime.DefaultHTTPErrorHandler)

	req, err := http.NewRequest("GET", "/test", nil)
	require.NoError(t, err)
	req.Header.Set("Accept", "application/json")

	_, outbound := grpcruntime.MarshalerForRequest(mux, req)
	jsonpb, ok := outbound.(*grpcruntime.JSONPb)
	require.True(t, ok)
	assert.True(t, jsonpb.MarshalOptions.EmitUnpopulated, "EmitUnpopulated should be true for snake case")

	req2, _ := http.NewRequest("GET", "/test", nil)
	req2.Header.Set("Accept", "application/json+camel")
	_, outbound2 := grpcruntime.MarshalerForRequest(mux, req2)
	jsonpb2, ok := outbound2.(*grpcruntime.JSONPb)
	require.True(t, ok)
	assert.True(t, jsonpb2.MarshalOptions.EmitUnpopulated, "EmitUnpopulated should be true for camel case")
}

func TestMakeGatewayMux_DiscardUnknown(t *testing.T) {
	s := &HttpServer{}
	mux := s.makeGatewayMux(grpcruntime.DefaultHTTPErrorHandler)

	req, err := http.NewRequest("GET", "/test", nil)
	require.NoError(t, err)
	req.Header.Set("Content-Type", "application/json")

	inbound, _ := grpcruntime.MarshalerForRequest(mux, req)
	jsonpb, ok := inbound.(*grpcruntime.JSONPb)
	require.True(t, ok)
	assert.True(t, jsonpb.UnmarshalOptions.DiscardUnknown, "DiscardUnknown should be true")
}
