package http

import (
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
)

func TestCORSPreflightShortCircuitsBusinessHandler(t *testing.T) {
	called := false
	handler := WithCORS(CORSConfig{
		Enabled:          true,
		AllowedOrigins:   []string{"https://global-interaction.com"},
		AllowedMethods:   []string{"POST", "OPTIONS"},
		AllowedHeaders:   []string{"Content-Type", "X-App-Code", "X-App-Version", "X-App-Build-Number"},
		ExposeHeaders:    []string{"X-Request-ID"},
		AllowCredentials: true,
		MaxAgeSec:        300,
	}, http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
		called = true
	}))

	request := httptest.NewRequest(http.MethodOptions, "/api/v1/auth/third-party/login", nil)
	request.Header.Set("Origin", "https://global-interaction.com")
	request.Header.Set("Access-Control-Request-Method", "POST")
	request.Header.Set("Access-Control-Request-Headers", "content-type,x-app-code,x-app-version,x-app-build-number")
	recorder := httptest.NewRecorder()

	handler.ServeHTTP(recorder, request)

	if called {
		t.Fatal("preflight must not reach business handler")
	}
	if recorder.Code != http.StatusNoContent {
		t.Fatalf("unexpected preflight status: %d", recorder.Code)
	}
	if got := recorder.Header().Get("Access-Control-Allow-Origin"); got != "https://global-interaction.com" {
		t.Fatalf("unexpected allow origin: %q", got)
	}
	if got := recorder.Header().Get("Access-Control-Allow-Credentials"); got != "true" {
		t.Fatalf("unexpected allow credentials: %q", got)
	}
	if got := recorder.Header().Get("Access-Control-Allow-Headers"); got != "Content-Type, X-App-Code, X-App-Version, X-App-Build-Number" {
		t.Fatalf("unexpected allow headers: %q", got)
	}
}

func TestCORSDefaultHeadersAllowLoginClientVersion(t *testing.T) {
	config := normalizeCORSConfig(CORSConfig{})
	allowedHeaders := "," + strings.ToLower(strings.Join(config.AllowedHeaders, ",")) + ","
	for _, header := range []string{"X-App-Version", "X-App-Build-Number"} {
		if !strings.Contains(allowedHeaders, ","+strings.ToLower(header)+",") {
			t.Fatalf("default CORS headers must allow browser login version metadata %q: %+v", header, config.AllowedHeaders)
		}
	}
}

func TestCORSRejectsDisallowedPreflightOrigin(t *testing.T) {
	called := false
	handler := WithCORS(CORSConfig{
		Enabled:        true,
		AllowedOrigins: []string{"https://global-interaction.com"},
	}, http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
		called = true
	}))

	request := httptest.NewRequest(http.MethodOptions, "/api/v1/auth/third-party/login", nil)
	request.Header.Set("Origin", "https://evil.example")
	request.Header.Set("Access-Control-Request-Method", "POST")
	recorder := httptest.NewRecorder()

	handler.ServeHTTP(recorder, request)

	if called {
		t.Fatal("denied preflight must not reach business handler")
	}
	if recorder.Code != http.StatusForbidden {
		t.Fatalf("unexpected status: %d", recorder.Code)
	}
	if got := recorder.Header().Get("Access-Control-Allow-Origin"); got != "" {
		t.Fatalf("disallowed origin must not be echoed: %q", got)
	}
}

func TestCORSAddsHeadersForAllowedActualRequest(t *testing.T) {
	handler := WithCORS(CORSConfig{
		Enabled:          true,
		AllowedOrigins:   []string{"https://global-interaction.com"},
		ExposeHeaders:    []string{"X-Request-ID"},
		AllowCredentials: true,
	}, http.HandlerFunc(func(writer http.ResponseWriter, _ *http.Request) {
		writer.Header().Set("X-Request-ID", "req-test")
		writer.WriteHeader(http.StatusOK)
	}))

	request := httptest.NewRequest(http.MethodPost, "/api/v1/auth/third-party/login", nil)
	request.Header.Set("Origin", "https://global-interaction.com")
	recorder := httptest.NewRecorder()

	handler.ServeHTTP(recorder, request)

	if recorder.Code != http.StatusOK {
		t.Fatalf("unexpected status: %d", recorder.Code)
	}
	if got := recorder.Header().Get("Access-Control-Allow-Origin"); got != "https://global-interaction.com" {
		t.Fatalf("unexpected allow origin: %q", got)
	}
	if got := recorder.Header().Get("Access-Control-Expose-Headers"); got != "X-Request-ID" {
		t.Fatalf("unexpected expose headers: %q", got)
	}
}

func TestCORSWildcardWithCredentialsEchoesAnyOrigin(t *testing.T) {
	called := false
	handler := WithCORS(CORSConfig{
		Enabled:          true,
		AllowedOrigins:   []string{"*"},
		AllowedMethods:   []string{"GET", "POST", "OPTIONS"},
		AllowedHeaders:   []string{"Authorization", "Content-Type", "X-App-Code"},
		AllowCredentials: true,
	}, http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
		called = true
	}))

	request := httptest.NewRequest(http.MethodOptions, "/api/v1/users/me/appearance", nil)
	request.Header.Set("Origin", "http://127.0.0.1:8081")
	request.Header.Set("Access-Control-Request-Method", "GET")
	request.Header.Set("Access-Control-Request-Headers", "authorization,x-app-code")
	recorder := httptest.NewRecorder()

	handler.ServeHTTP(recorder, request)

	if called {
		t.Fatal("wildcard preflight must not reach business handler")
	}
	if recorder.Code != http.StatusNoContent {
		t.Fatalf("unexpected wildcard preflight status: %d", recorder.Code)
	}
	if got := recorder.Header().Get("Access-Control-Allow-Origin"); got != "http://127.0.0.1:8081" {
		t.Fatalf("wildcard with credentials must echo request origin, got %q", got)
	}
	if got := recorder.Header().Get("Access-Control-Allow-Credentials"); got != "true" {
		t.Fatalf("unexpected allow credentials: %q", got)
	}
	if got := recorder.Header().Get("Access-Control-Expose-Headers"); !strings.Contains(got, "X-Hyapp-Access-Token") {
		t.Fatalf("renewed token header must be exposed to browser javascript, got %q", got)
	}
}
