//go:build e2e
// +build e2e

package e2e

import (
	"io"
	"net/http"
	"os"
	"testing"
	"time"

	"github.com/golang-jwt/jwt/v5"
	"github.com/rede5/gohorsejobs/backend/internal/database"
)

// TestE2E_Users_List tests listing users with RBAC
func TestE2E_Users_List(t *testing.T) {
	client := newTestClient()
	companyID, userID := setupTestCompanyAndUser(t) // Creates a SuperAdmin user (roles: superadmin)
	defer cleanupTestCompanyAndUser(t, companyID, userID)

	// =====================
	// 1. Unauthenticated Request
	// =====================
	t.Run("Unauthenticated", func(t *testing.T) {
		client.setAuthToken("") // Clear token
		resp, err := client.get("/api/v1/users")
		if err != nil {
			t.Fatalf("Failed to make request: %v", err)
		}
		defer resp.Body.Close()

		if resp.StatusCode != http.StatusUnauthorized {
			t.Errorf("Expected status 401, got %d", resp.StatusCode)
		}
	})

	// =====================
	// 2. Authenticated as SuperAdmin (Authorized)
	// =====================
	t.Run("AsSuperAdmin", func(t *testing.T) {
		// createAuthToken creates a token with 'superadmin' role by default in jobs_e2e_test.go
		// We can reuse it or use our new helper.
		// Let's use our explicit helper to be sure.
		token := createTokenWithRoles(t, userID, companyID, []string{"superadmin"})
		client.setAuthToken(token)

		resp, err := client.get("/api/v1/users")
		if err != nil {
			t.Fatalf("Failed to make request: %v", err)
		}
		defer resp.Body.Close()

		if resp.StatusCode != http.StatusOK {
			body, _ := io.ReadAll(resp.Body)
			t.Errorf("Expected status 200, got %d. Body: %s", resp.StatusCode, string(body))
		}
	})

	// =====================
	// 2a. Authenticated as Admin (Authorized)
	// =====================
	t.Run("AsAdmin", func(t *testing.T) {
		token := createTokenWithRoles(t, userID, companyID, []string{"admin"})
		client.setAuthToken(token)

		resp, err := client.get("/api/v1/users")
		if err != nil {
			t.Fatalf("Failed to make request: %v", err)
		}
		defer resp.Body.Close()

		if resp.StatusCode != http.StatusOK {
			t.Errorf("Expected status 200, got %d", resp.StatusCode)
		}
	})

	// =====================
	// 3. Authenticated as Candidate (Forbidden)
	// =====================
	t.Run("AsCandidate", func(t *testing.T) {
		// Create a candidate user
		candID := createTestCandidate(t)
		defer database.DB.Exec("DELETE FROM users WHERE id = $1", candID)

		token := createTokenWithRoles(t, candID, companyID, []string{"candidate"})
		client.setAuthToken(token)

		resp, err := client.get("/api/v1/users")
		if err != nil {
			t.Fatalf("Failed to make request: %v", err)
		}
		defer resp.Body.Close()

		if resp.StatusCode != http.StatusForbidden {
			t.Errorf("Expected status 403, got %d", resp.StatusCode)
		}
	})

	// =====================
	// 4. Authenticated as Recruiter (Forbidden - assuming Recruiters can't list all users)
	// =====================
	t.Run("AsRecruiter", func(t *testing.T) {
		// Uses same user/company for simplicity, just different claim
		token := createTokenWithRoles(t, userID, companyID, []string{"recruiter"})
		client.setAuthToken(token)

		resp, err := client.get("/api/v1/users")
		if err != nil {
			t.Fatalf("Failed to make request: %v", err)
		}
		defer resp.Body.Close()

		if resp.StatusCode != http.StatusForbidden {
			t.Errorf("Expected status 403, got %d", resp.StatusCode)
		}
	})
}

// Helper to create a candidate user
func createTestCandidate(t *testing.T) string {
	var id string
	query := `
		INSERT INTO users (identifier, password_hash, role, full_name, email, status, created_at, updated_at)
		VALUES ('e2e_candidate_' || gen_random_uuid(), 'hash', 'candidate', 'E2E Candidate', 'cand@e2e.com', 'active', NOW(), NOW())
		RETURNING id
	`
	err := database.DB.QueryRow(query).Scan(&id)
	if err != nil {
		t.Fatalf("Failed to create candidate: %v", err)
	}
	// Insert role
	database.DB.Exec("INSERT INTO user_roles (user_id, role) VALUES ($1, 'candidate')", id)
	return id
}

func createTokenWithRoles(t *testing.T, userID, tenantID string, roles []string) string {
	secret := os.Getenv("JWT_SECRET")
	if secret == "" {
		secret = "gohorse-super-secret-key-2024-production"
	}
	claims := jwt.MapClaims{
		"sub":    userID,
		"tenant": tenantID,
		"roles":  roles,
		"iss":    "gohorse-jobs",
		"exp":    time.Now().Add(time.Hour).Unix(),
		"iat":    time.Now().Unix(),
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	tokenStr, err := token.SignedString([]byte(secret))
	if err != nil {
		t.Fatalf("Failed to generate token: %v", err)
	}
	return tokenStr
}
