package security

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

	"github.com/gorilla/mux"
	"golang.org/x/oauth2"
)

func TestConfig_IsValid(t *testing.T) {
	c := &Config{
		Basic: nil,
		OIDC:  nil,
	}
	if c.IsValid() {
		t.Error("expected empty config to be valid")
	}
}

func TestConfig_ApplySecurityMiddleware(t *testing.T) {
	///////////
	// BASIC //
	///////////
	// Bcrypt
	c := &Config{Basic: &BasicConfig{
		Username:                        "john.doe",
		PasswordBcryptHashBase64Encoded: "JDJhJDA4JDFoRnpPY1hnaFl1OC9ISlFsa21VS09wOGlPU1ZOTDlHZG1qeTFvb3dIckRBUnlHUmNIRWlT",
	}}
	api := mux.NewRouter()
	api.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
	})
	if err := c.ApplySecurityMiddleware(api); err != nil {
		t.Error("expected no error, but was", err)
	}
	// Try to access the route without basic auth
	request, _ := http.NewRequest("GET", "/test", http.NoBody)
	responseRecorder := httptest.NewRecorder()
	api.ServeHTTP(responseRecorder, request)
	if responseRecorder.Code != http.StatusUnauthorized {
		t.Error("expected code to be 401, but was", responseRecorder.Code)
	}
	// Try again, but with basic auth
	request, _ = http.NewRequest("GET", "/test", http.NoBody)
	responseRecorder = httptest.NewRecorder()
	request.SetBasicAuth("john.doe", "hunter2")
	api.ServeHTTP(responseRecorder, request)
	if responseRecorder.Code != http.StatusOK {
		t.Error("expected code to be 200, but was", responseRecorder.Code)
	}
	//////////
	// OIDC //
	//////////
	api = mux.NewRouter()
	api.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
	})
	c.OIDC = &OIDCConfig{
		IssuerURL:       "https://sso.gatus.io/",
		RedirectURL:     "http://localhost:80/authorization-code/callback",
		Scopes:          []string{"openid"},
		AllowedSubjects: []string{"user1@example.com"},
		oauth2Config:    oauth2.Config{},
		verifier:        nil,
	}
	c.Basic = nil
	if err := c.ApplySecurityMiddleware(api); err != nil {
		t.Error("expected no error, but was", err)
	}
	// Try without any session cookie
	request, _ = http.NewRequest("GET", "/test", http.NoBody)
	responseRecorder = httptest.NewRecorder()
	api.ServeHTTP(responseRecorder, request)
	if responseRecorder.Code != http.StatusUnauthorized {
		t.Error("expected code to be 401, but was", responseRecorder.Code)
	}
	// Try with a session cookie
	request, _ = http.NewRequest("GET", "/test", http.NoBody)
	request.AddCookie(&http.Cookie{Name: "session", Value: "123"})
	responseRecorder = httptest.NewRecorder()
	api.ServeHTTP(responseRecorder, request)
	if responseRecorder.Code != http.StatusUnauthorized {
		t.Error("expected code to be 401, but was", responseRecorder.Code)
	}
}

func TestConfig_RegisterHandlers(t *testing.T) {
	c := &Config{}
	router := mux.NewRouter()
	c.RegisterHandlers(router)
	// Try to access the OIDC handler. This should fail, because the security config doesn't have OIDC
	request, _ := http.NewRequest("GET", "/oidc/login", http.NoBody)
	responseRecorder := httptest.NewRecorder()
	router.ServeHTTP(responseRecorder, request)
	if responseRecorder.Code != http.StatusNotFound {
		t.Error("expected code to be 404, but was", responseRecorder.Code)
	}
	// Set an empty OIDC config. This should fail, because the IssuerURL is required.
	c.OIDC = &OIDCConfig{}
	if err := c.RegisterHandlers(router); err == nil {
		t.Fatal("expected an error, but got none")
	}
	// Set the OIDC config and try again
	c.OIDC = &OIDCConfig{
		IssuerURL:       "https://sso.gatus.io/",
		RedirectURL:     "http://localhost:80/authorization-code/callback",
		Scopes:          []string{"openid"},
		AllowedSubjects: []string{"user1@example.com"},
	}
	if err := c.RegisterHandlers(router); err != nil {
		t.Fatal("expected no error, but got", err)
	}
	request, _ = http.NewRequest("GET", "/oidc/login", http.NoBody)
	responseRecorder = httptest.NewRecorder()
	router.ServeHTTP(responseRecorder, request)
	if responseRecorder.Code != http.StatusFound {
		t.Error("expected code to be 302, but was", responseRecorder.Code)
	}
}