package port_test

import (
	"io"
	"net/http"
	"net/url"
	"slices"
	"testing"

	"gitea.dwysokinski.me/twhelp/corev3/internal/port"
	"github.com/brianvoe/gofakeit/v6"
	"github.com/getkin/kin-openapi/openapi3"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

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

	t.Run("OK: openapi3.json", func(t *testing.T) {
		t.Parallel()

		u1, err := url.Parse(gofakeit.URL())
		u1.Path = ""
		require.NoError(t, err)

		u2, err := url.Parse(gofakeit.URL())
		u2.Path = ""
		require.NoError(t, err)

		servers := []port.OpenAPIConfigServer{
			{
				URL: u1,
			},
			{
				URL: u2,
			},
		}

		basePath := "/api"

		handler := newAPIHTTPHandler(t, func(cfg *apiHTTPHandlerConfig) {
			cfg.options = append(cfg.options, port.WithOpenAPIConfig(port.OpenAPIConfig{
				Enabled:  true,
				BasePath: basePath,
				Servers:  servers,
			}))
		})

		//nolint:bodyclose
		resp := doRequest(handler, http.MethodGet, "/v2/openapi3.json", nil)
		defer require.NoError(t, resp.Body.Close())
		assert.Equal(t, http.StatusOK, resp.StatusCode)
		b, err := io.ReadAll(resp.Body)
		require.NoError(t, err)
		body, err := openapi3.NewLoader().LoadFromData(b)
		require.NoError(t, err)
		for i, expected := range servers {
			idx := slices.IndexFunc(body.Servers, func(s *openapi3.Server) bool {
				return s.URL == expected.URL.String()+basePath
			})
			assert.GreaterOrEqualf(t, idx, 0, "expected[%d] not found", i)
		}
	})

	t.Run("OK: swagger", func(t *testing.T) {
		t.Parallel()

		handler := newAPIHTTPHandler(t, func(cfg *apiHTTPHandlerConfig) {
			cfg.options = append(cfg.options, port.WithOpenAPIConfig(port.OpenAPIConfig{
				Enabled:        true,
				SwaggerEnabled: true,
			}))
		})

		tests := []struct {
			target              string
			expectedContentType string
		}{
			{
				target:              "/v2/swagger/",
				expectedContentType: "text/html; charset=utf-8",
			},
			{
				target:              "/v2/swagger/swagger-initializer.js",
				expectedContentType: "text/javascript; charset=utf-8",
			},
		}

		for _, tt := range tests {
			t.Run(tt.target, func(t *testing.T) {
				t.Parallel()

				resp := doRequest(handler, http.MethodGet, tt.target, nil)
				defer require.NoError(t, resp.Body.Close())
				assert.Equal(t, http.StatusOK, resp.StatusCode)
				assert.Equal(t, tt.expectedContentType, resp.Header.Get("Content-Type"))
				_, _ = io.Copy(io.Discard, resp.Body)
			})
		}
	})
}
