package api

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

	"github.com/google/uuid"
	"github.com/mish/dealspace/lib"
)

// setupTestUser creates a user directly in the DB and returns a config with backdoor enabled
func setupTestUser(t *testing.T, db *lib.DB, cfg *lib.Config, email, name string) {
	t.Helper()
	now := time.Now().UnixMilli()
	user := &lib.User{
		UserID:    uuid.New().String(),
		Email:     email,
		Name:      name,
		Password:  "",
		Active:    true,
		CreatedAt: now,
		UpdatedAt: now,
	}
	if err := lib.UserCreate(db, user); err != nil {
		t.Fatalf("create test user: %v", err)
	}
}

// loginWithBackdoor uses the challenge/verify flow with backdoor code
func loginWithBackdoor(t *testing.T, client *http.Client, serverURL, email string) string {
	t.Helper()

	// Step 1: Send challenge
	challengeBody, _ := json.Marshal(map[string]string{"email": email})
	resp, err := client.Post(serverURL+"/api/auth/challenge", "application/json", bytes.NewReader(challengeBody))
	if err != nil {
		t.Fatalf("challenge request failed: %v", err)
	}
	resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		t.Fatalf("challenge expected 200, got %d", resp.StatusCode)
	}

	// Step 2: Verify with backdoor code
	verifyBody, _ := json.Marshal(map[string]string{"email": email, "code": "220402"})
	resp, err = client.Post(serverURL+"/api/auth/verify", "application/json", bytes.NewReader(verifyBody))
	if err != nil {
		t.Fatalf("verify request failed: %v", err)
	}
	var verifyResp map[string]interface{}
	json.NewDecoder(resp.Body).Decode(&verifyResp)
	resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		t.Fatalf("verify expected 200, got %d: %v", resp.StatusCode, verifyResp)
	}

	token, ok := verifyResp["token"].(string)
	if !ok || token == "" {
		t.Fatal("verify response should contain token")
	}
	return token
}

func TestFullFlow(t *testing.T) {
	tmpFile, err := os.CreateTemp("", "dealspace-integration-test-*.db")
	if err != nil {
		t.Fatalf("create temp file: %v", err)
	}
	tmpFile.Close()
	defer os.Remove(tmpFile.Name())

	db, err := lib.OpenDB(tmpFile.Name(), "../migrations/001_initial.sql")
	if err != nil {
		t.Fatalf("OpenDB: %v", err)
	}
	defer db.Close()

	masterKey := make([]byte, 32)
	for i := range masterKey {
		masterKey[i] = byte(i)
	}
	jwtSecret := []byte("test-jwt-secret-32-bytes-long!!")

	cfg := &lib.Config{
		MasterKey:    masterKey,
		JWTSecret:    jwtSecret,
		Env:          "development",
		BackdoorCode: "220402",
	}

	tmpDir, err := os.MkdirTemp("", "dealspace-store-test")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(tmpDir)
	store, _ := lib.NewLocalStore(tmpDir)

	router := NewRouter(db, cfg, store, nil, nil, nil)
	server := httptest.NewServer(router)
	defer server.Close()

	client := &http.Client{}

	// Create test user directly in DB
	setupTestUser(t, db, cfg, "admin@test.com", "Admin User")

	// Step 1: Login with challenge/verify + backdoor
	t.Log("Step 1: Login via challenge/verify")
	token := loginWithBackdoor(t, client, server.URL, "admin@test.com")
	t.Logf("Got token: %s...", token[:20])

	// Wrong code should fail
	wrongVerify, _ := json.Marshal(map[string]string{"email": "admin@test.com", "code": "000000"})
	resp, _ := client.Post(server.URL+"/api/auth/verify", "application/json", bytes.NewReader(wrongVerify))
	if resp.StatusCode != http.StatusUnauthorized {
		t.Errorf("wrong code should return 401, got %d", resp.StatusCode)
	}
	resp.Body.Close()

	// Step 2: GET /api/auth/me
	t.Log("Step 2: Get current user")
	req, _ := http.NewRequest("GET", server.URL+"/api/auth/me", nil)
	req.Header.Set("Authorization", "Bearer "+token)
	resp, err = client.Do(req)
	if err != nil {
		t.Fatalf("me request failed: %v", err)
	}
	if resp.StatusCode != http.StatusOK {
		t.Fatalf("me expected 200, got %d", resp.StatusCode)
	}
	var meResp map[string]interface{}
	json.NewDecoder(resp.Body).Decode(&meResp)
	resp.Body.Close()

	if meResp["email"] != "admin@test.com" {
		t.Errorf("me response email mismatch: got %s", meResp["email"])
	}

	// Step 3: POST /api/projects
	t.Log("Step 3: Create project")
	projectBody, _ := json.Marshal(map[string]string{
		"name":      "Test Deal Project",
		"deal_type": "M&A",
	})
	req, _ = http.NewRequest("POST", server.URL+"/api/projects", bytes.NewReader(projectBody))
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("Content-Type", "application/json")
	resp, err = client.Do(req)
	if err != nil {
		t.Fatalf("create project request failed: %v", err)
	}
	if resp.StatusCode != http.StatusCreated {
		var errResp map[string]string
		json.NewDecoder(resp.Body).Decode(&errResp)
		t.Fatalf("create project expected 201, got %d: %v", resp.StatusCode, errResp)
	}
	var projectResp map[string]interface{}
	json.NewDecoder(resp.Body).Decode(&projectResp)
	resp.Body.Close()

	projectID := projectResp["project_id"].(string)
	if projectID == "" {
		t.Fatal("project response should contain project_id")
	}
	t.Logf("Created project: %s", projectID)

	// Step 4: GET /api/projects
	t.Log("Step 4: List projects")
	req, _ = http.NewRequest("GET", server.URL+"/api/projects", nil)
	req.Header.Set("Authorization", "Bearer "+token)
	resp, err = client.Do(req)
	if err != nil {
		t.Fatalf("list projects request failed: %v", err)
	}
	if resp.StatusCode != http.StatusOK {
		t.Fatalf("list projects expected 200, got %d", resp.StatusCode)
	}
	var listResp []lib.Entry
	json.NewDecoder(resp.Body).Decode(&listResp)
	resp.Body.Close()

	if len(listResp) < 1 {
		t.Errorf("expected at least 1 project, got %d", len(listResp))
	}

	// Step 5: Logout
	t.Log("Step 5: Logout")
	req, _ = http.NewRequest("POST", server.URL+"/api/auth/logout", nil)
	req.Header.Set("Authorization", "Bearer "+token)
	resp, err = client.Do(req)
	if err != nil {
		t.Fatalf("logout request failed: %v", err)
	}
	if resp.StatusCode != http.StatusOK {
		t.Fatalf("logout expected 200, got %d", resp.StatusCode)
	}
	resp.Body.Close()

	// Step 6: Token should be invalid after logout
	t.Log("Step 6: Verify token invalidated")
	req, _ = http.NewRequest("GET", server.URL+"/api/auth/me", nil)
	req.Header.Set("Authorization", "Bearer "+token)
	resp, err = client.Do(req)
	if err != nil {
		t.Fatalf("me after logout request failed: %v", err)
	}
	if resp.StatusCode != http.StatusUnauthorized {
		t.Fatalf("me after logout expected 401, got %d", resp.StatusCode)
	}
	resp.Body.Close()

	t.Log("Full flow test passed!")
}

func TestHealthEndpoint(t *testing.T) {
	tmpFile, _ := os.CreateTemp("", "dealspace-health-test-*.db")
	tmpFile.Close()
	defer os.Remove(tmpFile.Name())

	db, _ := lib.OpenDB(tmpFile.Name(), "../migrations/001_initial.sql")
	defer db.Close()

	cfg := &lib.Config{
		MasterKey: make([]byte, 32),
		JWTSecret: []byte("test-secret"),
	}

	router := NewRouter(db, cfg, nil, nil, nil, nil)
	server := httptest.NewServer(router)
	defer server.Close()

	resp, err := http.Get(server.URL + "/health")
	if err != nil {
		t.Fatalf("health request failed: %v", err)
	}
	if resp.StatusCode != http.StatusOK {
		t.Errorf("health expected 200, got %d", resp.StatusCode)
	}

	var healthResp map[string]string
	json.NewDecoder(resp.Body).Decode(&healthResp)
	resp.Body.Close()

	if healthResp["status"] != "ok" {
		t.Errorf("health status should be 'ok', got %s", healthResp["status"])
	}
}

func TestUnauthenticatedAccess(t *testing.T) {
	tmpFile, _ := os.CreateTemp("", "dealspace-unauth-test-*.db")
	tmpFile.Close()
	defer os.Remove(tmpFile.Name())

	db, _ := lib.OpenDB(tmpFile.Name(), "../migrations/001_initial.sql")
	defer db.Close()

	cfg := &lib.Config{
		MasterKey: make([]byte, 32),
		JWTSecret: []byte("test-secret"),
	}

	router := NewRouter(db, cfg, nil, nil, nil, nil)
	server := httptest.NewServer(router)
	defer server.Close()

	endpoints := []struct {
		method string
		path   string
	}{
		{"GET", "/api/auth/me"},
		{"POST", "/api/auth/logout"},
		{"GET", "/api/projects"},
		{"POST", "/api/projects"},
		{"GET", "/api/projects/test/entries"},
	}

	for _, ep := range endpoints {
		req, _ := http.NewRequest(ep.method, server.URL+ep.path, nil)
		resp, err := http.DefaultClient.Do(req)
		if err != nil {
			t.Errorf("%s %s: request failed: %v", ep.method, ep.path, err)
			continue
		}
		if resp.StatusCode != http.StatusUnauthorized {
			t.Errorf("%s %s: expected 401, got %d", ep.method, ep.path, resp.StatusCode)
		}
		resp.Body.Close()
	}
}

func TestEntryOperations(t *testing.T) {
	tmpFile, _ := os.CreateTemp("", "dealspace-entry-test-*.db")
	tmpFile.Close()
	defer os.Remove(tmpFile.Name())

	db, _ := lib.OpenDB(tmpFile.Name(), "../migrations/001_initial.sql")
	defer db.Close()

	masterKey := make([]byte, 32)
	jwtSecret := []byte("test-secret-32-bytes!!")

	cfg := &lib.Config{
		MasterKey:    masterKey,
		JWTSecret:    jwtSecret,
		Env:          "development",
		BackdoorCode: "220402",
	}

	tmpDir, _ := os.MkdirTemp("", "dealspace-store-entry-test")
	defer os.RemoveAll(tmpDir)
	store, _ := lib.NewLocalStore(tmpDir)

	router := NewRouter(db, cfg, store, nil, nil, nil)
	server := httptest.NewServer(router)
	defer server.Close()

	client := &http.Client{}

	// Create user and login via challenge/verify with backdoor
	setupTestUser(t, db, cfg, "entry@test.com", "Entry Test")
	token := loginWithBackdoor(t, client, server.URL, "entry@test.com")

	// Create project
	projectBody, _ := json.Marshal(map[string]string{"name": "Entry Test Project"})
	req, _ := http.NewRequest("POST", server.URL+"/api/projects", bytes.NewReader(projectBody))
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("Content-Type", "application/json")
	resp, _ := client.Do(req)
	var projectResp map[string]interface{}
	json.NewDecoder(resp.Body).Decode(&projectResp)
	resp.Body.Close()
	projectID := projectResp["project_id"].(string)

	// Create entry
	entryBody, _ := json.Marshal(map[string]interface{}{
		"project_id": projectID,
		"type":       "request",
		"depth":      1,
		"summary":    "Test Request",
		"data":       `{"question": "What is the revenue?"}`,
		"stage":      "pre_dataroom",
	})
	req, _ = http.NewRequest("POST", server.URL+"/api/projects/"+projectID+"/entries", bytes.NewReader(entryBody))
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("Content-Type", "application/json")
	resp, _ = client.Do(req)
	if resp.StatusCode != http.StatusCreated {
		var errResp map[string]string
		json.NewDecoder(resp.Body).Decode(&errResp)
		t.Fatalf("create entry expected 201, got %d: %v", resp.StatusCode, errResp)
	}
	var entryResp lib.Entry
	json.NewDecoder(resp.Body).Decode(&entryResp)
	resp.Body.Close()
	entryID := entryResp.EntryID

	if entryID == "" {
		t.Fatal("entry should have ID")
	}

	// List entries
	req, _ = http.NewRequest("GET", server.URL+"/api/projects/"+projectID+"/entries?type=request", nil)
	req.Header.Set("Authorization", "Bearer "+token)
	resp, _ = client.Do(req)
	if resp.StatusCode != http.StatusOK {
		t.Fatalf("list entries expected 200, got %d", resp.StatusCode)
	}
	var entries []lib.Entry
	json.NewDecoder(resp.Body).Decode(&entries)
	resp.Body.Close()

	if len(entries) != 1 {
		t.Errorf("expected 1 entry, got %d", len(entries))
	}

	// Update entry
	updateBody, _ := json.Marshal(map[string]interface{}{
		"project_id": projectID,
		"type":       "request",
		"depth":      1,
		"summary":    "Updated Request",
		"stage":      "dataroom",
		"version":    1,
	})
	req, _ = http.NewRequest("PUT", server.URL+"/api/projects/"+projectID+"/entries/"+entryID, bytes.NewReader(updateBody))
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("Content-Type", "application/json")
	resp, _ = client.Do(req)
	if resp.StatusCode != http.StatusOK {
		var errResp map[string]string
		json.NewDecoder(resp.Body).Decode(&errResp)
		t.Fatalf("update entry expected 200, got %d: %v", resp.StatusCode, errResp)
	}
	resp.Body.Close()

	// Delete entry
	req, _ = http.NewRequest("DELETE", server.URL+"/api/projects/"+projectID+"/entries/"+entryID, nil)
	req.Header.Set("Authorization", "Bearer "+token)
	resp, _ = client.Do(req)
	if resp.StatusCode != http.StatusOK {
		t.Fatalf("delete entry expected 200, got %d", resp.StatusCode)
	}
	resp.Body.Close()

	// Verify deleted
	req, _ = http.NewRequest("GET", server.URL+"/api/projects/"+projectID+"/entries?type=request", nil)
	req.Header.Set("Authorization", "Bearer "+token)
	resp, _ = client.Do(req)
	json.NewDecoder(resp.Body).Decode(&entries)
	resp.Body.Close()

	if len(entries) != 0 {
		t.Errorf("expected 0 entries after delete, got %d", len(entries))
	}
}

func TestChallengeVerifyFlow(t *testing.T) {
	tmpFile, _ := os.CreateTemp("", "dealspace-challenge-test-*.db")
	tmpFile.Close()
	defer os.Remove(tmpFile.Name())

	db, _ := lib.OpenDB(tmpFile.Name(), "../migrations/001_initial.sql")
	defer db.Close()

	cfg := &lib.Config{
		MasterKey:    make([]byte, 32),
		JWTSecret:    []byte("test-jwt-secret-32-bytes-long!!"),
		Env:          "development",
		BackdoorCode: "220402",
	}

	router := NewRouter(db, cfg, nil, nil, nil, nil)
	server := httptest.NewServer(router)
	defer server.Close()

	client := &http.Client{}

	// Challenge for non-existent user should still return 200 (no info leak)
	body, _ := json.Marshal(map[string]string{"email": "nobody@test.com"})
	resp, _ := client.Post(server.URL+"/api/auth/challenge", "application/json", bytes.NewReader(body))
	if resp.StatusCode != http.StatusOK {
		t.Errorf("challenge for unknown email should return 200, got %d", resp.StatusCode)
	}
	resp.Body.Close()

	// Verify for non-existent user should fail
	body, _ = json.Marshal(map[string]string{"email": "nobody@test.com", "code": "220402"})
	resp, _ = client.Post(server.URL+"/api/auth/verify", "application/json", bytes.NewReader(body))
	if resp.StatusCode != http.StatusUnauthorized {
		t.Errorf("verify for unknown user should return 401, got %d", resp.StatusCode)
	}
	resp.Body.Close()

	// Create user and test backdoor login
	setupTestUser(t, db, cfg, "test@test.com", "Test User")
	token := loginWithBackdoor(t, client, server.URL, "test@test.com")
	if token == "" {
		t.Fatal("backdoor login should return token")
	}

	// Verify the token works
	req, _ := http.NewRequest("GET", server.URL+"/api/auth/me", nil)
	req.Header.Set("Authorization", "Bearer "+token)
	resp, _ = client.Do(req)
	if resp.StatusCode != http.StatusOK {
		t.Errorf("me with valid token should return 200, got %d", resp.StatusCode)
	}
	resp.Body.Close()
}

// ---- Additional integration tests ----

func TestFullDealWorkflow(t *testing.T) {
	tmpFile, err := os.CreateTemp("", "dealspace-deal-workflow-*.db")
	if err != nil {
		t.Fatalf("create temp file: %v", err)
	}
	tmpFile.Close()
	defer os.Remove(tmpFile.Name())

	db, err := lib.OpenDB(tmpFile.Name(), "../migrations/001_initial.sql")
	if err != nil {
		t.Fatalf("OpenDB: %v", err)
	}
	defer db.Close()

	masterKey := make([]byte, 32)
	for i := range masterKey {
		masterKey[i] = byte(i)
	}
	jwtSecret := []byte("test-jwt-secret-32-bytes-long!!")

	cfg := &lib.Config{
		MasterKey:    masterKey,
		JWTSecret:    jwtSecret,
		Env:          "development",
		BackdoorCode: "220402",
	}

	tmpDir, _ := os.MkdirTemp("", "dealspace-deal-workflow-store")
	defer os.RemoveAll(tmpDir)
	store, _ := lib.NewLocalStore(tmpDir)

	router := NewRouter(db, cfg, store, nil, nil, nil)
	server := httptest.NewServer(router)
	defer server.Close()

	client := &http.Client{}

	// Step 1: Create IB admin user
	t.Log("Step 1: Create IB admin user")
	setupTestUser(t, db, cfg, "ibadmin@workflow.com", "IB Admin")
	ibToken := loginWithBackdoor(t, client, server.URL, "ibadmin@workflow.com")

	// Step 2: Create a project
	t.Log("Step 2: Create project")
	projectBody, _ := json.Marshal(map[string]string{
		"name":      "Acme Acquisition",
		"deal_type": "M&A",
	})
	req, _ := http.NewRequest("POST", server.URL+"/api/projects", bytes.NewReader(projectBody))
	req.Header.Set("Authorization", "Bearer "+ibToken)
	req.Header.Set("Content-Type", "application/json")
	resp, _ := client.Do(req)
	if resp.StatusCode != http.StatusCreated {
		t.Fatalf("create project expected 201, got %d", resp.StatusCode)
	}
	var projectResp map[string]interface{}
	json.NewDecoder(resp.Body).Decode(&projectResp)
	resp.Body.Close()
	projectID := projectResp["project_id"].(string)
	t.Logf("Created project: %s", projectID)

	// Step 3: Create an organization
	t.Log("Step 3: Create organization")
	orgBody, _ := json.Marshal(map[string]interface{}{
		"name":    "Acme Corp",
		"domains": []string{"acme.com", "acmecorp.com"},
		"role":    "seller",
	})
	req, _ = http.NewRequest("POST", server.URL+"/api/orgs", bytes.NewReader(orgBody))
	req.Header.Set("Authorization", "Bearer "+ibToken)
	req.Header.Set("Content-Type", "application/json")
	resp, _ = client.Do(req)
	if resp.StatusCode != http.StatusCreated {
		var errResp map[string]string
		json.NewDecoder(resp.Body).Decode(&errResp)
		t.Fatalf("create org expected 201, got %d: %v", resp.StatusCode, errResp)
	}
	var orgResp map[string]interface{}
	json.NewDecoder(resp.Body).Decode(&orgResp)
	resp.Body.Close()
	orgID := orgResp["entry_id"].(string)
	t.Logf("Created org: %s", orgID)

	// Step 4: Link org to project (deal_org)
	t.Log("Step 4: Link org to project")
	dealOrgBody, _ := json.Marshal(map[string]interface{}{
		"org_id":      orgID,
		"role":        "seller",
		"domain_lock": true,
	})
	req, _ = http.NewRequest("POST", server.URL+"/api/projects/"+projectID+"/orgs", bytes.NewReader(dealOrgBody))
	req.Header.Set("Authorization", "Bearer "+ibToken)
	req.Header.Set("Content-Type", "application/json")
	resp, _ = client.Do(req)
	if resp.StatusCode != http.StatusCreated {
		var errResp map[string]string
		json.NewDecoder(resp.Body).Decode(&errResp)
		t.Fatalf("create deal_org expected 201, got %d: %v", resp.StatusCode, errResp)
	}
	var dealOrgResp map[string]interface{}
	json.NewDecoder(resp.Body).Decode(&dealOrgResp)
	resp.Body.Close()
	t.Logf("Linked org to project: %v", dealOrgResp["deal_org_id"])

	// Step 5: List deal orgs and verify domain is shown
	t.Log("Step 5: List deal orgs")
	req, _ = http.NewRequest("GET", server.URL+"/api/projects/"+projectID+"/orgs", nil)
	req.Header.Set("Authorization", "Bearer "+ibToken)
	resp, _ = client.Do(req)
	if resp.StatusCode != http.StatusOK {
		t.Fatalf("list deal orgs expected 200, got %d", resp.StatusCode)
	}
	var dealOrgs []map[string]interface{}
	json.NewDecoder(resp.Body).Decode(&dealOrgs)
	resp.Body.Close()

	if len(dealOrgs) != 1 {
		t.Fatalf("expected 1 deal org, got %d", len(dealOrgs))
	}

	dealOrg := dealOrgs[0]
	if dealOrg["org_name"] != "Acme Corp" {
		t.Errorf("org_name mismatch: got %v", dealOrg["org_name"])
	}
	orgDomains, ok := dealOrg["org_domains"].([]interface{})
	if !ok || len(orgDomains) != 2 {
		t.Errorf("org_domains should have 2 entries: got %v", dealOrg["org_domains"])
	}
	if dealOrg["role"] != "seller" {
		t.Errorf("role mismatch: got %v", dealOrg["role"])
	}
	if dealOrg["domain_lock"] != true {
		t.Errorf("domain_lock should be true: got %v", dealOrg["domain_lock"])
	}

	t.Log("Full deal workflow test passed!")
}

func TestSuperAdminSeeAllProjects(t *testing.T) {
	tmpFile, err := os.CreateTemp("", "dealspace-superadmin-projects-*.db")
	if err != nil {
		t.Fatalf("create temp file: %v", err)
	}
	tmpFile.Close()
	defer os.Remove(tmpFile.Name())

	db, err := lib.OpenDB(tmpFile.Name(), "../migrations/001_initial.sql")
	if err != nil {
		t.Fatalf("OpenDB: %v", err)
	}
	defer db.Close()

	masterKey := make([]byte, 32)
	for i := range masterKey {
		masterKey[i] = byte(i)
	}
	jwtSecret := []byte("test-jwt-secret-32-bytes-long!!")

	cfg := &lib.Config{
		MasterKey:    masterKey,
		JWTSecret:    jwtSecret,
		Env:          "development",
		BackdoorCode: "220402",
	}

	tmpDir, _ := os.MkdirTemp("", "dealspace-superadmin-store")
	defer os.RemoveAll(tmpDir)
	store, _ := lib.NewLocalStore(tmpDir)

	router := NewRouter(db, cfg, store, nil, nil, nil)
	server := httptest.NewServer(router)
	defer server.Close()

	client := &http.Client{}
	now := time.Now().UnixMilli()

	// Create User A and their project
	t.Log("Create User A and Project A")
	setupTestUser(t, db, cfg, "usera@test.com", "User A")
	userAToken := loginWithBackdoor(t, client, server.URL, "usera@test.com")

	projectABody, _ := json.Marshal(map[string]string{"name": "Project A"})
	req, _ := http.NewRequest("POST", server.URL+"/api/projects", bytes.NewReader(projectABody))
	req.Header.Set("Authorization", "Bearer "+userAToken)
	req.Header.Set("Content-Type", "application/json")
	resp, _ := client.Do(req)
	resp.Body.Close()

	// Create User B and their project
	t.Log("Create User B and Project B")
	setupTestUser(t, db, cfg, "userb@test.com", "User B")
	userBToken := loginWithBackdoor(t, client, server.URL, "userb@test.com")

	projectBBody, _ := json.Marshal(map[string]string{"name": "Project B"})
	req, _ = http.NewRequest("POST", server.URL+"/api/projects", bytes.NewReader(projectBBody))
	req.Header.Set("Authorization", "Bearer "+userBToken)
	req.Header.Set("Content-Type", "application/json")
	resp, _ = client.Do(req)
	resp.Body.Close()

	// Create super admin (no explicit project grants except super_admin role)
	t.Log("Create Super Admin")
	saID := uuid.New().String()
	saUser := &lib.User{
		UserID:    saID,
		Email:     "superadmin@test.com",
		Name:      "Super Admin",
		Password:  "",
		Active:    true,
		CreatedAt: now,
		UpdatedAt: now,
	}
	lib.UserCreate(db, saUser)
	lib.AccessGrant(db, &lib.Access{
		ID:        uuid.New().String(),
		ProjectID: "global",
		UserID:    saID,
		Role:      lib.RoleSuperAdmin,
		Ops:       "rwdm",
		CanGrant:  true,
		GrantedBy: "system",
		GrantedAt: now,
	})
	saToken := loginWithBackdoor(t, client, server.URL, "superadmin@test.com")

	// User A should only see Project A
	t.Log("Verify User A sees only Project A")
	req, _ = http.NewRequest("GET", server.URL+"/api/projects", nil)
	req.Header.Set("Authorization", "Bearer "+userAToken)
	resp, _ = client.Do(req)
	var userAProjects []lib.Entry
	json.NewDecoder(resp.Body).Decode(&userAProjects)
	resp.Body.Close()

	if len(userAProjects) != 1 {
		t.Errorf("User A should see 1 project, got %d", len(userAProjects))
	}

	// User B should only see Project B
	t.Log("Verify User B sees only Project B")
	req, _ = http.NewRequest("GET", server.URL+"/api/projects", nil)
	req.Header.Set("Authorization", "Bearer "+userBToken)
	resp, _ = client.Do(req)
	var userBProjects []lib.Entry
	json.NewDecoder(resp.Body).Decode(&userBProjects)
	resp.Body.Close()

	if len(userBProjects) != 1 {
		t.Errorf("User B should see 1 project, got %d", len(userBProjects))
	}

	// Super admin should see ALL projects (via admin endpoint)
	t.Log("Verify Super Admin sees all projects")
	req, _ = http.NewRequest("GET", server.URL+"/api/admin/projects", nil)
	req.Header.Set("Authorization", "Bearer "+saToken)
	resp, _ = client.Do(req)
	if resp.StatusCode != http.StatusOK {
		t.Fatalf("admin/projects expected 200, got %d", resp.StatusCode)
	}
	var saProjects []lib.Entry
	json.NewDecoder(resp.Body).Decode(&saProjects)
	resp.Body.Close()

	if len(saProjects) < 2 {
		t.Errorf("Super admin should see at least 2 projects, got %d", len(saProjects))
	}

	// Verify super admin can access Project A directly (even without explicit grant)
	t.Log("Verify Super Admin can access Project A detail")
	// Get project A's ID
	projectAID := ""
	for _, p := range saProjects {
		if p.SummaryText == "Project A" {
			projectAID = p.ProjectID
			break
		}
	}
	if projectAID == "" {
		// Fall back to finding via user A's projects
		projectAID = userAProjects[0].ProjectID
	}

	req, _ = http.NewRequest("GET", server.URL+"/api/projects/"+projectAID+"/detail", nil)
	req.Header.Set("Authorization", "Bearer "+saToken)
	resp, _ = client.Do(req)
	if resp.StatusCode != http.StatusOK {
		t.Errorf("super admin should access Project A detail, got %d", resp.StatusCode)
	}
	resp.Body.Close()

	t.Log("Super admin project visibility test passed!")
}
