package handlers

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

	"github.com/Wikid82/charon/backend/internal/config"
	"github.com/Wikid82/charon/backend/internal/models"
	"github.com/Wikid82/charon/backend/internal/services"
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

func setupAuthHandler(t *testing.T) (*AuthHandler, *gorm.DB) {
	dbName := "file:" + t.Name() + "?mode=memory&cache=shared"
	db, err := gorm.Open(sqlite.Open(dbName), &gorm.Config{})
	require.NoError(t, err)
	db.AutoMigrate(&models.User{}, &models.Setting{})

	cfg := config.Config{JWTSecret: "test-secret"}
	authService := services.NewAuthService(db, cfg)
	return NewAuthHandler(authService), db
}

func TestAuthHandler_Login(t *testing.T) {
	handler, db := setupAuthHandler(t)

	// Create user
	user := &models.User{
		UUID:  uuid.NewString(),
		Email: "test@example.com",
		Name:  "Test User",
	}
	user.SetPassword("password123")
	db.Create(user)

	gin.SetMode(gin.TestMode)
	r := gin.New()
	r.POST("/login", handler.Login)

	// Success
	body := map[string]string{
		"email":    "test@example.com",
		"password": "password123",
	}
	jsonBody, _ := json.Marshal(body)
	req := httptest.NewRequest("POST", "/login", bytes.NewBuffer(jsonBody))
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)
	assert.Contains(t, w.Body.String(), "token")
}

func TestSetSecureCookie_HTTPS_Strict(t *testing.T) {
	gin.SetMode(gin.TestMode)
	os.Setenv("CHARON_ENV", "production")
	defer os.Unsetenv("CHARON_ENV")
	recorder := httptest.NewRecorder()
	ctx, _ := gin.CreateTestContext(recorder)
	req := httptest.NewRequest("POST", "https://example.com/login", http.NoBody)
	ctx.Request = req

	setSecureCookie(ctx, "auth_token", "abc", 60)
	cookies := recorder.Result().Cookies()
	require.Len(t, cookies, 1)
	c := cookies[0]
	assert.True(t, c.Secure)
	assert.Equal(t, http.SameSiteStrictMode, c.SameSite)
}

func TestSetSecureCookie_HTTP_Lax(t *testing.T) {
	gin.SetMode(gin.TestMode)
	recorder := httptest.NewRecorder()
	ctx, _ := gin.CreateTestContext(recorder)
	req := httptest.NewRequest("POST", "http://192.0.2.10/login", http.NoBody)
	req.Header.Set("X-Forwarded-Proto", "http")
	ctx.Request = req

	setSecureCookie(ctx, "auth_token", "abc", 60)
	cookies := recorder.Result().Cookies()
	require.Len(t, cookies, 1)
	c := cookies[0]
	assert.False(t, c.Secure)
	assert.Equal(t, http.SameSiteLaxMode, c.SameSite)
}

func TestAuthHandler_Login_Errors(t *testing.T) {
	handler, _ := setupAuthHandler(t)
	gin.SetMode(gin.TestMode)
	r := gin.New()
	r.POST("/login", handler.Login)

	// 1. Invalid JSON
	req := httptest.NewRequest("POST", "/login", bytes.NewBufferString("invalid"))
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)
	assert.Equal(t, http.StatusBadRequest, w.Code)

	// 2. Invalid Credentials
	body := map[string]string{
		"email":    "nonexistent@example.com",
		"password": "wrong",
	}
	jsonBody, _ := json.Marshal(body)
	req = httptest.NewRequest("POST", "/login", bytes.NewBuffer(jsonBody))
	req.Header.Set("Content-Type", "application/json")
	w = httptest.NewRecorder()
	r.ServeHTTP(w, req)
	assert.Equal(t, http.StatusUnauthorized, w.Code)
}

func TestAuthHandler_Register(t *testing.T) {
	handler, _ := setupAuthHandler(t)

	gin.SetMode(gin.TestMode)
	r := gin.New()
	r.POST("/register", handler.Register)

	body := map[string]string{
		"email":    "new@example.com",
		"password": "password123",
		"name":     "New User",
	}
	jsonBody, _ := json.Marshal(body)
	req := httptest.NewRequest("POST", "/register", bytes.NewBuffer(jsonBody))
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	assert.Equal(t, http.StatusCreated, w.Code)
	assert.Contains(t, w.Body.String(), "new@example.com")
}

func TestAuthHandler_Register_Duplicate(t *testing.T) {
	handler, db := setupAuthHandler(t)
	db.Create(&models.User{UUID: uuid.NewString(), Email: "dup@example.com", Name: "Dup"})

	gin.SetMode(gin.TestMode)
	r := gin.New()
	r.POST("/register", handler.Register)

	body := map[string]string{
		"email":    "dup@example.com",
		"password": "password123",
		"name":     "Dup User",
	}
	jsonBody, _ := json.Marshal(body)
	req := httptest.NewRequest("POST", "/register", bytes.NewBuffer(jsonBody))
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	assert.Equal(t, http.StatusInternalServerError, w.Code)
}

func TestAuthHandler_Logout(t *testing.T) {
	handler, _ := setupAuthHandler(t)

	gin.SetMode(gin.TestMode)
	r := gin.New()
	r.POST("/logout", handler.Logout)

	req := httptest.NewRequest("POST", "/logout", http.NoBody)
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)
	assert.Contains(t, w.Body.String(), "Logged out")
	// Check cookie
	cookie := w.Result().Cookies()[0]
	assert.Equal(t, "auth_token", cookie.Name)
	assert.Equal(t, -1, cookie.MaxAge)
}

func TestAuthHandler_Me(t *testing.T) {
	handler, db := setupAuthHandler(t)

	// Create user that matches the middleware ID
	user := &models.User{
		UUID:  uuid.NewString(),
		Email: "me@example.com",
		Name:  "Me User",
		Role:  "admin",
	}
	db.Create(user)

	gin.SetMode(gin.TestMode)
	r := gin.New()
	// Simulate middleware
	r.Use(func(c *gin.Context) {
		c.Set("userID", user.ID)
		c.Set("role", user.Role)
		c.Next()
	})
	r.GET("/me", handler.Me)

	req := httptest.NewRequest("GET", "/me", http.NoBody)
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)
	var resp map[string]interface{}
	json.Unmarshal(w.Body.Bytes(), &resp)
	assert.Equal(t, float64(user.ID), resp["user_id"])
	assert.Equal(t, "admin", resp["role"])
	assert.Equal(t, "Me User", resp["name"])
	assert.Equal(t, "me@example.com", resp["email"])
}

func TestAuthHandler_Me_NotFound(t *testing.T) {
	handler, _ := setupAuthHandler(t)
	gin.SetMode(gin.TestMode)
	r := gin.New()
	r.Use(func(c *gin.Context) {
		c.Set("userID", uint(999)) // Non-existent ID
		c.Next()
	})
	r.GET("/me", handler.Me)

	req := httptest.NewRequest("GET", "/me", http.NoBody)
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	assert.Equal(t, http.StatusNotFound, w.Code)
}

func TestAuthHandler_ChangePassword(t *testing.T) {
	handler, db := setupAuthHandler(t)

	// Create user
	user := &models.User{
		UUID:  uuid.NewString(),
		Email: "change@example.com",
		Name:  "Change User",
	}
	user.SetPassword("oldpassword")
	db.Create(user)

	gin.SetMode(gin.TestMode)
	r := gin.New()
	// Simulate middleware
	r.Use(func(c *gin.Context) {
		c.Set("userID", user.ID)
		c.Next()
	})
	r.POST("/change-password", handler.ChangePassword)

	body := map[string]string{
		"old_password": "oldpassword",
		"new_password": "newpassword123",
	}
	jsonBody, _ := json.Marshal(body)
	req := httptest.NewRequest("POST", "/change-password", bytes.NewBuffer(jsonBody))
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)
	assert.Contains(t, w.Body.String(), "Password updated successfully")

	// Verify password changed
	var updatedUser models.User
	db.First(&updatedUser, user.ID)
	assert.True(t, updatedUser.CheckPassword("newpassword123"))
}

func TestAuthHandler_ChangePassword_WrongOld(t *testing.T) {
	handler, db := setupAuthHandler(t)
	user := &models.User{UUID: uuid.NewString(), Email: "wrong@example.com"}
	user.SetPassword("correct")
	db.Create(user)

	gin.SetMode(gin.TestMode)
	r := gin.New()
	r.Use(func(c *gin.Context) {
		c.Set("userID", user.ID)
		c.Next()
	})
	r.POST("/change-password", handler.ChangePassword)

	body := map[string]string{
		"old_password": "wrong",
		"new_password": "newpassword",
	}
	jsonBody, _ := json.Marshal(body)
	req := httptest.NewRequest("POST", "/change-password", bytes.NewBuffer(jsonBody))
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	assert.Equal(t, http.StatusBadRequest, w.Code)
}

func TestAuthHandler_ChangePassword_Errors(t *testing.T) {
	handler, _ := setupAuthHandler(t)
	gin.SetMode(gin.TestMode)
	r := gin.New()
	r.POST("/change-password", handler.ChangePassword)

	// 1. BindJSON error (checked before auth)
	req, _ := http.NewRequest("POST", "/change-password", bytes.NewBufferString("invalid json"))
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)
	assert.Equal(t, http.StatusBadRequest, w.Code)

	// 2. Unauthorized (valid JSON but no user in context)
	body := map[string]string{
		"old_password": "oldpassword",
		"new_password": "newpassword123",
	}
	jsonBody, _ := json.Marshal(body)
	req, _ = http.NewRequest("POST", "/change-password", bytes.NewBuffer(jsonBody))
	req.Header.Set("Content-Type", "application/json")
	w = httptest.NewRecorder()
	r.ServeHTTP(w, req)
	assert.Equal(t, http.StatusUnauthorized, w.Code)
}

// setupAuthHandlerWithDB creates an AuthHandler with DB access for forward auth tests
func setupAuthHandlerWithDB(t *testing.T) (*AuthHandler, *gorm.DB) {
	dbName := "file:" + t.Name() + "?mode=memory&cache=shared"
	db, err := gorm.Open(sqlite.Open(dbName), &gorm.Config{})
	require.NoError(t, err)
	db.AutoMigrate(&models.User{}, &models.Setting{}, &models.ProxyHost{})

	cfg := config.Config{JWTSecret: "test-secret"}
	authService := services.NewAuthService(db, cfg)
	return NewAuthHandlerWithDB(authService, db), db
}

func TestNewAuthHandlerWithDB(t *testing.T) {
	handler, db := setupAuthHandlerWithDB(t)
	assert.NotNil(t, handler)
	assert.NotNil(t, handler.db)
	assert.NotNil(t, db)
}

func TestAuthHandler_Verify_NoCookie(t *testing.T) {
	handler, _ := setupAuthHandlerWithDB(t)
	gin.SetMode(gin.TestMode)
	r := gin.New()
	r.GET("/verify", handler.Verify)

	req := httptest.NewRequest("GET", "/verify", http.NoBody)
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	assert.Equal(t, http.StatusUnauthorized, w.Code)
	assert.Equal(t, "/login", w.Header().Get("X-Auth-Redirect"))
}

func TestAuthHandler_Verify_InvalidToken(t *testing.T) {
	handler, _ := setupAuthHandlerWithDB(t)
	gin.SetMode(gin.TestMode)
	r := gin.New()
	r.GET("/verify", handler.Verify)

	req := httptest.NewRequest("GET", "/verify", http.NoBody)
	req.AddCookie(&http.Cookie{Name: "auth_token", Value: "invalid-token"})
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	assert.Equal(t, http.StatusUnauthorized, w.Code)
}

func TestAuthHandler_Verify_ValidToken(t *testing.T) {
	handler, db := setupAuthHandlerWithDB(t)

	// Create user
	user := &models.User{
		UUID:    uuid.NewString(),
		Email:   "test@example.com",
		Name:    "Test User",
		Role:    "user",
		Enabled: true,
	}
	user.SetPassword("password123")
	db.Create(user)

	// Generate token
	token, _ := handler.authService.GenerateToken(user)

	gin.SetMode(gin.TestMode)
	r := gin.New()
	r.GET("/verify", handler.Verify)

	req := httptest.NewRequest("GET", "/verify", http.NoBody)
	req.AddCookie(&http.Cookie{Name: "auth_token", Value: token})
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)
	assert.Equal(t, "test@example.com", w.Header().Get("X-Forwarded-User"))
	assert.Equal(t, "user", w.Header().Get("X-Forwarded-Groups"))
}

func TestAuthHandler_Verify_BearerToken(t *testing.T) {
	handler, db := setupAuthHandlerWithDB(t)

	user := &models.User{
		UUID:    uuid.NewString(),
		Email:   "bearer@example.com",
		Name:    "Bearer User",
		Role:    "admin",
		Enabled: true,
	}
	user.SetPassword("password123")
	db.Create(user)

	token, _ := handler.authService.GenerateToken(user)

	gin.SetMode(gin.TestMode)
	r := gin.New()
	r.GET("/verify", handler.Verify)

	req := httptest.NewRequest("GET", "/verify", http.NoBody)
	req.Header.Set("Authorization", "Bearer "+token)
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)
	assert.Equal(t, "bearer@example.com", w.Header().Get("X-Forwarded-User"))
}

func TestAuthHandler_Verify_DisabledUser(t *testing.T) {
	handler, db := setupAuthHandlerWithDB(t)

	user := &models.User{
		UUID:  uuid.NewString(),
		Email: "disabled@example.com",
		Name:  "Disabled User",
		Role:  "user",
	}
	user.SetPassword("password123")
	db.Create(user)
	// Explicitly disable after creation to bypass GORM's default:true behavior
	db.Model(user).Update("enabled", false)

	token, _ := handler.authService.GenerateToken(user)

	gin.SetMode(gin.TestMode)
	r := gin.New()
	r.GET("/verify", handler.Verify)

	req := httptest.NewRequest("GET", "/verify", http.NoBody)
	req.AddCookie(&http.Cookie{Name: "auth_token", Value: token})
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	assert.Equal(t, http.StatusUnauthorized, w.Code)
}

func TestAuthHandler_Verify_ForwardAuthDenied(t *testing.T) {
	handler, db := setupAuthHandlerWithDB(t)

	// Create proxy host with forward auth enabled
	proxyHost := &models.ProxyHost{
		UUID:               uuid.NewString(),
		Name:               "Protected App",
		DomainNames:        "app.example.com",
		ForwardAuthEnabled: true,
		Enabled:            true,
	}
	db.Create(proxyHost)

	// Create user with deny_all permission
	user := &models.User{
		UUID:           uuid.NewString(),
		Email:          "denied@example.com",
		Name:           "Denied User",
		Role:           "user",
		Enabled:        true,
		PermissionMode: models.PermissionModeDenyAll,
	}
	user.SetPassword("password123")
	db.Create(user)

	token, _ := handler.authService.GenerateToken(user)

	gin.SetMode(gin.TestMode)
	r := gin.New()
	r.GET("/verify", handler.Verify)

	req := httptest.NewRequest("GET", "/verify", http.NoBody)
	req.AddCookie(&http.Cookie{Name: "auth_token", Value: token})
	req.Header.Set("X-Forwarded-Host", "app.example.com")
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	assert.Equal(t, http.StatusForbidden, w.Code)
}

func TestAuthHandler_VerifyStatus_NotAuthenticated(t *testing.T) {
	handler, _ := setupAuthHandlerWithDB(t)
	gin.SetMode(gin.TestMode)
	r := gin.New()
	r.GET("/status", handler.VerifyStatus)

	req := httptest.NewRequest("GET", "/status", http.NoBody)
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)
	var resp map[string]interface{}
	json.Unmarshal(w.Body.Bytes(), &resp)
	assert.Equal(t, false, resp["authenticated"])
}

func TestAuthHandler_VerifyStatus_InvalidToken(t *testing.T) {
	handler, _ := setupAuthHandlerWithDB(t)
	gin.SetMode(gin.TestMode)
	r := gin.New()
	r.GET("/status", handler.VerifyStatus)

	req := httptest.NewRequest("GET", "/status", http.NoBody)
	req.AddCookie(&http.Cookie{Name: "auth_token", Value: "invalid"})
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)
	var resp map[string]interface{}
	json.Unmarshal(w.Body.Bytes(), &resp)
	assert.Equal(t, false, resp["authenticated"])
}

func TestAuthHandler_VerifyStatus_Authenticated(t *testing.T) {
	handler, db := setupAuthHandlerWithDB(t)

	user := &models.User{
		UUID:    uuid.NewString(),
		Email:   "status@example.com",
		Name:    "Status User",
		Role:    "user",
		Enabled: true,
	}
	user.SetPassword("password123")
	db.Create(user)

	token, _ := handler.authService.GenerateToken(user)

	gin.SetMode(gin.TestMode)
	r := gin.New()
	r.GET("/status", handler.VerifyStatus)

	req := httptest.NewRequest("GET", "/status", http.NoBody)
	req.AddCookie(&http.Cookie{Name: "auth_token", Value: token})
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)
	var resp map[string]interface{}
	json.Unmarshal(w.Body.Bytes(), &resp)
	assert.Equal(t, true, resp["authenticated"])
	userObj := resp["user"].(map[string]interface{})
	assert.Equal(t, "status@example.com", userObj["email"])
}

func TestAuthHandler_VerifyStatus_DisabledUser(t *testing.T) {
	handler, db := setupAuthHandlerWithDB(t)

	user := &models.User{
		UUID:  uuid.NewString(),
		Email: "disabled2@example.com",
		Name:  "Disabled User 2",
		Role:  "user",
	}
	user.SetPassword("password123")
	db.Create(user)
	// Explicitly disable after creation to bypass GORM's default:true behavior
	db.Model(user).Update("enabled", false)

	token, _ := handler.authService.GenerateToken(user)

	gin.SetMode(gin.TestMode)
	r := gin.New()
	r.GET("/status", handler.VerifyStatus)

	req := httptest.NewRequest("GET", "/status", http.NoBody)
	req.AddCookie(&http.Cookie{Name: "auth_token", Value: token})
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)
	var resp map[string]interface{}
	json.Unmarshal(w.Body.Bytes(), &resp)
	assert.Equal(t, false, resp["authenticated"])
}

func TestAuthHandler_GetAccessibleHosts_Unauthorized(t *testing.T) {
	handler, _ := setupAuthHandlerWithDB(t)
	gin.SetMode(gin.TestMode)
	r := gin.New()
	r.GET("/hosts", handler.GetAccessibleHosts)

	req := httptest.NewRequest("GET", "/hosts", http.NoBody)
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	assert.Equal(t, http.StatusUnauthorized, w.Code)
}

func TestAuthHandler_GetAccessibleHosts_AllowAll(t *testing.T) {
	handler, db := setupAuthHandlerWithDB(t)

	// Create proxy hosts
	host1 := &models.ProxyHost{UUID: uuid.NewString(), Name: "Host 1", DomainNames: "host1.example.com", Enabled: true}
	host2 := &models.ProxyHost{UUID: uuid.NewString(), Name: "Host 2", DomainNames: "host2.example.com", Enabled: true}
	db.Create(host1)
	db.Create(host2)

	user := &models.User{
		UUID:           uuid.NewString(),
		Email:          "allowall@example.com",
		Name:           "Allow All User",
		Role:           "user",
		Enabled:        true,
		PermissionMode: models.PermissionModeAllowAll,
	}
	db.Create(user)

	gin.SetMode(gin.TestMode)
	r := gin.New()
	r.Use(func(c *gin.Context) {
		c.Set("userID", user.ID)
		c.Next()
	})
	r.GET("/hosts", handler.GetAccessibleHosts)

	req := httptest.NewRequest("GET", "/hosts", http.NoBody)
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)
	var resp map[string]interface{}
	json.Unmarshal(w.Body.Bytes(), &resp)
	hosts := resp["hosts"].([]interface{})
	assert.Len(t, hosts, 2)
}

func TestAuthHandler_GetAccessibleHosts_DenyAll(t *testing.T) {
	handler, db := setupAuthHandlerWithDB(t)

	// Create proxy hosts
	host1 := &models.ProxyHost{UUID: uuid.NewString(), Name: "Host 1", DomainNames: "host1.example.com", Enabled: true}
	db.Create(host1)

	user := &models.User{
		UUID:           uuid.NewString(),
		Email:          "denyall@example.com",
		Name:           "Deny All User",
		Role:           "user",
		Enabled:        true,
		PermissionMode: models.PermissionModeDenyAll,
	}
	db.Create(user)

	gin.SetMode(gin.TestMode)
	r := gin.New()
	r.Use(func(c *gin.Context) {
		c.Set("userID", user.ID)
		c.Next()
	})
	r.GET("/hosts", handler.GetAccessibleHosts)

	req := httptest.NewRequest("GET", "/hosts", http.NoBody)
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)
	var resp map[string]interface{}
	json.Unmarshal(w.Body.Bytes(), &resp)
	hosts := resp["hosts"].([]interface{})
	assert.Len(t, hosts, 0)
}

func TestAuthHandler_GetAccessibleHosts_PermittedHosts(t *testing.T) {
	handler, db := setupAuthHandlerWithDB(t)

	// Create proxy hosts
	host1 := &models.ProxyHost{UUID: uuid.NewString(), Name: "Host 1", DomainNames: "host1.example.com", Enabled: true}
	host2 := &models.ProxyHost{UUID: uuid.NewString(), Name: "Host 2", DomainNames: "host2.example.com", Enabled: true}
	db.Create(host1)
	db.Create(host2)

	user := &models.User{
		UUID:           uuid.NewString(),
		Email:          "permitted@example.com",
		Name:           "Permitted User",
		Role:           "user",
		Enabled:        true,
		PermissionMode: models.PermissionModeDenyAll,
		PermittedHosts: []models.ProxyHost{*host1}, // Only host1
	}
	db.Create(user)

	gin.SetMode(gin.TestMode)
	r := gin.New()
	r.Use(func(c *gin.Context) {
		c.Set("userID", user.ID)
		c.Next()
	})
	r.GET("/hosts", handler.GetAccessibleHosts)

	req := httptest.NewRequest("GET", "/hosts", http.NoBody)
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)
	var resp map[string]interface{}
	json.Unmarshal(w.Body.Bytes(), &resp)
	hosts := resp["hosts"].([]interface{})
	assert.Len(t, hosts, 1)
}

func TestAuthHandler_GetAccessibleHosts_UserNotFound(t *testing.T) {
	handler, _ := setupAuthHandlerWithDB(t)

	gin.SetMode(gin.TestMode)
	r := gin.New()
	r.Use(func(c *gin.Context) {
		c.Set("userID", uint(99999))
		c.Next()
	})
	r.GET("/hosts", handler.GetAccessibleHosts)

	req := httptest.NewRequest("GET", "/hosts", http.NoBody)
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	assert.Equal(t, http.StatusNotFound, w.Code)
}

func TestAuthHandler_CheckHostAccess_Unauthorized(t *testing.T) {
	handler, _ := setupAuthHandlerWithDB(t)
	gin.SetMode(gin.TestMode)
	r := gin.New()
	r.GET("/hosts/:hostId/access", handler.CheckHostAccess)

	req := httptest.NewRequest("GET", "/hosts/1/access", http.NoBody)
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	assert.Equal(t, http.StatusUnauthorized, w.Code)
}

func TestAuthHandler_CheckHostAccess_InvalidHostID(t *testing.T) {
	handler, db := setupAuthHandlerWithDB(t)

	user := &models.User{UUID: uuid.NewString(), Email: "check@example.com", Enabled: true}
	db.Create(user)

	gin.SetMode(gin.TestMode)
	r := gin.New()
	r.Use(func(c *gin.Context) {
		c.Set("userID", user.ID)
		c.Next()
	})
	r.GET("/hosts/:hostId/access", handler.CheckHostAccess)

	req := httptest.NewRequest("GET", "/hosts/invalid/access", http.NoBody)
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	assert.Equal(t, http.StatusBadRequest, w.Code)
}

func TestAuthHandler_CheckHostAccess_Allowed(t *testing.T) {
	handler, db := setupAuthHandlerWithDB(t)

	host := &models.ProxyHost{UUID: uuid.NewString(), Name: "Test Host", DomainNames: "test.example.com", Enabled: true}
	db.Create(host)

	user := &models.User{
		UUID:           uuid.NewString(),
		Email:          "checkallowed@example.com",
		Enabled:        true,
		PermissionMode: models.PermissionModeAllowAll,
	}
	db.Create(user)

	gin.SetMode(gin.TestMode)
	r := gin.New()
	r.Use(func(c *gin.Context) {
		c.Set("userID", user.ID)
		c.Next()
	})
	r.GET("/hosts/:hostId/access", handler.CheckHostAccess)

	req := httptest.NewRequest("GET", "/hosts/1/access", http.NoBody)
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)
	var resp map[string]interface{}
	json.Unmarshal(w.Body.Bytes(), &resp)
	assert.Equal(t, true, resp["can_access"])
}

func TestAuthHandler_CheckHostAccess_Denied(t *testing.T) {
	handler, db := setupAuthHandlerWithDB(t)

	host := &models.ProxyHost{UUID: uuid.NewString(), Name: "Protected Host", DomainNames: "protected.example.com", Enabled: true}
	db.Create(host)

	user := &models.User{
		UUID:           uuid.NewString(),
		Email:          "checkdenied@example.com",
		Enabled:        true,
		PermissionMode: models.PermissionModeDenyAll,
	}
	db.Create(user)

	gin.SetMode(gin.TestMode)
	r := gin.New()
	r.Use(func(c *gin.Context) {
		c.Set("userID", user.ID)
		c.Next()
	})
	r.GET("/hosts/:hostId/access", handler.CheckHostAccess)

	req := httptest.NewRequest("GET", "/hosts/1/access", http.NoBody)
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)
	var resp map[string]interface{}
	json.Unmarshal(w.Body.Bytes(), &resp)
	assert.Equal(t, false, resp["can_access"])
}
