package graph

import (
	"encoding/json"
	"fmt"
	"strings"
	"testing"
	"time"

	"github.com/99designs/gqlgen/client"
	"github.com/99designs/gqlgen/graphql/handler"
	"github.com/bradleyjkemp/cupaloy/v2"
	"gogs.dmsc.dev/arp/auth"
	"gogs.dmsc.dev/arp/graph/model"
	"gogs.dmsc.dev/arp/graph/testutil"
	"gorm.io/gorm"
)

var snapshotter = cupaloy.New(cupaloy.SnapshotSubdirectory("testdata/snapshots"))

type TestClient struct {
	client *client.Client
	db     *gorm.DB
	token  string
}

type IDTracker struct {
	Permissions  map[string]string
	Roles        map[string]string
	Users        map[string]string
	TaskStatuses map[string]string
	Services     map[string]string
	Tasks        map[string]string
	Notes        map[string]string
	Messages     []string
}

func NewIDTracker() *IDTracker {
	return &IDTracker{
		Permissions:  make(map[string]string),
		Roles:        make(map[string]string),
		Users:        make(map[string]string),
		TaskStatuses: make(map[string]string),
		Services:     make(map[string]string),
		Tasks:        make(map[string]string),
		Notes:        make(map[string]string),
		Messages:     make([]string, 0),
	}
}

func setupTestClient(t *testing.T) (*TestClient, *IDTracker) {
	// Setup and bootstrap the database with initial data from init_tests.sql
	db, err := testutil.SetupAndBootstrapTestDB()
	if err != nil {
		t.Fatalf("Failed to setup test database: %v", err)
	}
	resolver := &Resolver{DB: db}
	schema := NewExecutableSchema(Config{Resolvers: resolver})
	srv := handler.NewDefaultServer(schema)

	// Wrap with auth middleware
	authSrv := auth.AuthMiddleware(srv)

	gqlClient := client.New(authSrv)

	// Login as admin to get a token
	var loginResponse struct {
		Login struct {
			Token string `json:"token"`
			User  struct {
				ID    string `json:"id"`
				Email string `json:"email"`
			} `json:"user"`
		} `json:"login"`
	}
	loginQuery := `mutation { login(email: "admin@example.com", password: "secret123") { token user { id email } } }`
	err = gqlClient.Post(loginQuery, &loginResponse)
	if err != nil {
		t.Fatalf("Failed to login as admin: %v", err)
	}
	if loginResponse.Login.Token == "" {
		t.Fatal("Login returned empty token")
	}

	// Create authenticated client
	authClient := client.New(authSrv, client.AddHeader("Authorization", "Bearer "+loginResponse.Login.Token))

	// Initialize tracker with bootstrapped data
	tracker := NewIDTracker()

	// Track bootstrapped admin user
	tracker.Users["admin@example.com"] = loginResponse.Login.User.ID

	// Fetch and track bootstrapped permissions
	var permsResponse struct {
		Permissions []struct {
			ID   string `json:"id"`
			Code string `json:"code"`
		} `json:"permissions"`
	}
	authClient.Post(`query { permissions { id code } }`, &permsResponse)
	for _, perm := range permsResponse.Permissions {
		tracker.Permissions[perm.Code] = perm.ID
	}

	// Fetch and track bootstrapped roles
	var rolesResponse struct {
		Roles []struct {
			ID   string `json:"id"`
			Name string `json:"name"`
		} `json:"roles"`
	}
	authClient.Post(`query { roles { id name } }`, &rolesResponse)
	for _, role := range rolesResponse.Roles {
		tracker.Roles[role.Name] = role.ID
	}

	// Fetch and track bootstrapped task statuses
	var statusesResponse struct {
		TaskStatuses []struct {
			ID   string `json:"id"`
			Code string `json:"code"`
		} `json:"taskStatuses"`
	}
	authClient.Post(`query { taskStatuses { id code } }`, &statusesResponse)
	for _, status := range statusesResponse.TaskStatuses {
		tracker.TaskStatuses[status.Code] = status.ID
	}

	return &TestClient{client: authClient, db: db, token: loginResponse.Login.Token}, tracker
}

func normalizeJSON(jsonStr string) string {
	var data interface{}
	if err := json.Unmarshal([]byte(jsonStr), &data); err != nil {
		return jsonStr
	}
	normalizeData(data)
	bytes, _ := json.MarshalIndent(data, "", "  ")
	return string(bytes)
}

func normalizeData(data interface{}) {
	switch v := data.(type) {
	case map[string]interface{}:
		delete(v, "id")
		delete(v, "ID")
		delete(v, "createdAt")
		delete(v, "updatedAt")
		delete(v, "sentAt")
		delete(v, "createdByID")
		delete(v, "userId")
		delete(v, "serviceId")
		delete(v, "statusId")
		delete(v, "assigneeId")
		delete(v, "conversationId")
		delete(v, "senderId")
		for _, val := range v {
			normalizeData(val)
		}
	case []interface{}:
		for _, item := range v {
			normalizeData(item)
		}
	}
}

func snapshotResult(t *testing.T, name string, jsonStr string) {
	normalized := normalizeJSON(jsonStr)
	snapshotter.SnapshotT(t, name, normalized)
}

func TestIntegration_Bootstrap(t *testing.T) {
	tc, tracker := setupTestClient(t)
	seed := testutil.GetSeedData()

	// Phase 1: Create Permissions (skip if already bootstrapped)
	t.Run("CreatePermissions", func(t *testing.T) {
		for _, perm := range seed.Permissions {
			// Skip if already exists from bootstrap
			if _, exists := tracker.Permissions[perm.Code]; exists {
				continue
			}
			var response struct {
				CreatePermission struct {
					ID          string `json:"id"`
					Code        string `json:"code"`
					Description string `json:"description"`
				} `json:"createPermission"`
			}
			query := fmt.Sprintf(`mutation { createPermission(input: {code: "%s", description: "%s"}) { id code description } }`, perm.Code, perm.Description)
			err := tc.client.Post(query, &response)
			if err != nil {
				t.Fatalf("Failed to create permission %s: %v", perm.Code, err)
			}
			tracker.Permissions[perm.Code] = response.CreatePermission.ID
		}
		var permsResponse struct {
			Permissions []interface{} `json:"permissions"`
		}
		tc.client.Post(`query { permissions { id code description } }`, &permsResponse)
		jsonBytes, _ := json.MarshalIndent(permsResponse, "", "  ")
		snapshotResult(t, "permissions", string(jsonBytes))
	})

	// Phase 2: Create Roles (skip if already bootstrapped)
	t.Run("CreateRoles", func(t *testing.T) {
		for _, role := range seed.Roles {
			// Skip if already exists from bootstrap
			if _, exists := tracker.Roles[role.Name]; exists {
				continue
			}
			permIDs := make([]string, len(role.PermissionCodes))
			for i, code := range role.PermissionCodes {
				permIDs[i] = tracker.Permissions[code]
			}
			var response struct {
				CreateRole struct {
					ID          string `json:"id"`
					Name        string `json:"name"`
					Description string `json:"description"`
				} `json:"createRole"`
			}
			query := fmt.Sprintf(`mutation { createRole(input: {name: "%s", description: "%s", permissions: ["%s"]}) { id name description } }`, role.Name, role.Description, strings.Join(permIDs, `", "`))
			err := tc.client.Post(query, &response)
			if err != nil {
				t.Fatalf("Failed to create role %s: %v", role.Name, err)
			}
			tracker.Roles[role.Name] = response.CreateRole.ID
		}
		var rolesResponse struct {
			Roles []interface{} `json:"roles"`
		}
		tc.client.Post(`query { roles { id name description permissions { id code } } }`, &rolesResponse)
		jsonBytes, _ := json.MarshalIndent(rolesResponse, "", "  ")
		snapshotResult(t, "roles", string(jsonBytes))
	})

	// Phase 3: Create Users (additional to bootstrapped admin)
	t.Run("CreateUsers", func(t *testing.T) {
		for _, user := range seed.Users {
			roleIDs := make([]string, len(user.RoleNames))
			for i, name := range user.RoleNames {
				roleIDs[i] = tracker.Roles[name]
			}
			var response struct {
				CreateUser struct {
					ID    string `json:"id"`
					Email string `json:"email"`
				} `json:"createUser"`
			}
			query := fmt.Sprintf(`mutation { createUser(input: {email: "%s", password: "%s", roles: ["%s"]}) { id email } }`, user.Email, user.Password, strings.Join(roleIDs, `", "`))
			err := tc.client.Post(query, &response)
			if err != nil {
				t.Fatalf("Failed to create user %s: %v", user.Email, err)
			}
			tracker.Users[user.Email] = response.CreateUser.ID
		}
		var usersResponse struct {
			Users []interface{} `json:"users"`
		}
		tc.client.Post(`query { users { id email roles { id name } } }`, &usersResponse)
		jsonBytes, _ := json.MarshalIndent(usersResponse, "", "  ")
		snapshotResult(t, "users", string(jsonBytes))
	})

	// Phase 4: Create Task Statuses (skip if already bootstrapped)
	t.Run("CreateTaskStatuses", func(t *testing.T) {
		for _, status := range seed.TaskStatuses {
			// Skip if already exists from bootstrap
			if _, exists := tracker.TaskStatuses[status.Code]; exists {
				continue
			}
			var response struct {
				CreateTaskStatus struct {
					ID    string `json:"id"`
					Code  string `json:"code"`
					Label string `json:"label"`
				} `json:"createTaskStatus"`
			}
			query := fmt.Sprintf(`mutation { createTaskStatus(input: {code: "%s", label: "%s"}) { id code label } }`, status.Code, status.Label)
			err := tc.client.Post(query, &response)
			if err != nil {
				t.Fatalf("Failed to create task status %s: %v", status.Code, err)
			}
			tracker.TaskStatuses[status.Code] = response.CreateTaskStatus.ID
		}
		var statusesResponse struct {
			TaskStatuses []interface{} `json:"taskStatuses"`
		}
		tc.client.Post(`query { taskStatuses { id code label } }`, &statusesResponse)
		jsonBytes, _ := json.MarshalIndent(statusesResponse, "", "  ")
		snapshotResult(t, "taskStatuses", string(jsonBytes))
	})

	// Phase 5: Create Services
	t.Run("CreateServices", func(t *testing.T) {
		for _, service := range seed.Services {
			participantIDs := make([]string, len(service.ParticipantEmails))
			for i, email := range service.ParticipantEmails {
				participantIDs[i] = tracker.Users[email]
			}
			var response struct {
				CreateService struct {
					ID   string `json:"id"`
					Name string `json:"name"`
				} `json:"createService"`
			}
			query := fmt.Sprintf(`mutation { createService(input: {name: "%s", description: "%s", createdById: "%s", participants: ["%s"]}) { id name } }`, service.Name, service.Description, tracker.Users[service.CreatorEmail], strings.Join(participantIDs, `", "`))
			err := tc.client.Post(query, &response)
			if err != nil {
				t.Fatalf("Failed to create service %s: %v", service.Name, err)
			}
			tracker.Services[service.Name] = response.CreateService.ID
		}
		var servicesResponse struct {
			Services []interface{} `json:"services"`
		}
		tc.client.Post(`query { services { id name description } }`, &servicesResponse)
		jsonBytes, _ := json.MarshalIndent(servicesResponse, "", "  ")
		snapshotResult(t, "services", string(jsonBytes))
	})

	// Phase 6: Create Tasks
	t.Run("CreateTasks", func(t *testing.T) {
		for _, task := range seed.Tasks {
			var response struct {
				CreateTask struct {
					ID    string `json:"id"`
					Title string `json:"title"`
				} `json:"createTask"`
			}
			var assigneeID string
			if task.AssigneeEmail != "" {
				assigneeID = tracker.Users[task.AssigneeEmail]
			}
			statusID := tracker.TaskStatuses[task.StatusCode]
			if assigneeID != "" {
				query := fmt.Sprintf(`mutation { createTask(input: {title: "%s", content: "%s", createdById: "%s", assigneeId: "%s", statusId: "%s", priority: "%s"}) { id title } }`, task.Title, task.Content, tracker.Users[task.CreatorEmail], assigneeID, statusID, task.Priority)
				err := tc.client.Post(query, &response)
				if err != nil {
					t.Fatalf("Failed to create task %s: %v", task.Title, err)
				}
			} else {
				query := fmt.Sprintf(`mutation { createTask(input: {title: "%s", content: "%s", createdById: "%s", statusId: "%s", priority: "%s"}) { id title } }`, task.Title, task.Content, tracker.Users[task.CreatorEmail], statusID, task.Priority)
				err := tc.client.Post(query, &response)
				if err != nil {
					t.Fatalf("Failed to create task %s: %v", task.Title, err)
				}
			}
			tracker.Tasks[task.Title] = response.CreateTask.ID
		}
		var tasksResponse struct {
			Tasks []interface{} `json:"tasks"`
		}
		tc.client.Post(`query { tasks { id title content priority } }`, &tasksResponse)
		jsonBytes, _ := json.MarshalIndent(tasksResponse, "", "  ")
		snapshotResult(t, "tasks", string(jsonBytes))
	})

	// Phase 7: Create Notes
	t.Run("CreateNotes", func(t *testing.T) {
		for _, note := range seed.Notes {
			var response struct {
				CreateNote struct {
					ID    string `json:"id"`
					Title string `json:"title"`
				} `json:"createNote"`
			}
			query := fmt.Sprintf(`mutation { createNote(input: {title: "%s", content: "%s", userId: "%s", serviceId: "%s"}) { id title } }`, note.Title, note.Content, tracker.Users[note.UserEmail], tracker.Services[note.ServiceName])
			err := tc.client.Post(query, &response)
			if err != nil {
				t.Fatalf("Failed to create note %s: %v", note.Title, err)
			}
			tracker.Notes[note.Title] = response.CreateNote.ID
		}
		var notesResponse struct {
			Notes []interface{} `json:"notes"`
		}
		tc.client.Post(`query { notes { id title content } }`, &notesResponse)
		jsonBytes, _ := json.MarshalIndent(notesResponse, "", "  ")
		snapshotResult(t, "notes", string(jsonBytes))
	})

	// Phase 8: Create Messages
	t.Run("CreateMessages", func(t *testing.T) {
		for _, msg := range seed.Messages {
			receiverIDs := make([]string, len(msg.ReceiverEmails))
			for i, email := range msg.ReceiverEmails {
				receiverIDs[i] = tracker.Users[email]
			}
			var response struct {
				CreateMessage struct {
					ID string `json:"id"`
				} `json:"createMessage"`
			}
			query := fmt.Sprintf(`mutation { createMessage(input: {content: "%s", receivers: ["%s"]}) { id } }`, msg.Content, strings.Join(receiverIDs, `", "`))
			err := tc.client.Post(query, &response)
			if err != nil {
				t.Fatalf("Failed to create message: %v", err)
			}
			tracker.Messages = append(tracker.Messages, response.CreateMessage.ID)
		}
		var messagesResponse struct {
			Messages []interface{} `json:"messages"`
		}
		tc.client.Post(`query { messages { id content receivers } }`, &messagesResponse)
		jsonBytes, _ := json.MarshalIndent(messagesResponse, "", "  ")
		snapshotResult(t, "messages", string(jsonBytes))
	})
}

// TestIntegration_Update tests update operations
func TestIntegration_Update(t *testing.T) {
	tc, tracker := setupTestClient(t)
	seed := testutil.GetSeedData()

	// Bootstrap first
	bootstrapData(t, tc, tracker, seed)

	// Update a user
	t.Run("UpdateUser", func(t *testing.T) {
		userID := tracker.Users["admin@example.com"]
		var response struct {
			UpdateUser struct {
				ID    string `json:"id"`
				Email string `json:"email"`
			} `json:"updateUser"`
		}
		query := fmt.Sprintf(`mutation { updateUser(id: "%s", input: {email: "admin-updated@example.com", password: "new-password", roles: ["%s"]}) { id email } }`, userID, tracker.Roles["admin"])
		err := tc.client.Post(query, &response)
		if err != nil {
			t.Fatalf("Failed to update user: %v", err)
		}
		var usersResponse struct {
			Users []interface{} `json:"users"`
		}
		tc.client.Post(`query { users { id email roles { id name } } }`, &usersResponse)
		jsonBytes, _ := json.MarshalIndent(usersResponse, "", "  ")
		snapshotResult(t, "users_after_update", string(jsonBytes))
	})

	// Update a task
	t.Run("UpdateTask", func(t *testing.T) {
		taskID := tracker.Tasks["Setup development environment"]
		var response struct {
			UpdateTask struct {
				ID    string `json:"id"`
				Title string `json:"title"`
			} `json:"updateTask"`
		}
		query := fmt.Sprintf(`mutation { updateTask(id: "%s", input: {title: "Setup development environment - COMPLETE", priority: "low"}) { id title } }`, taskID)
		err := tc.client.Post(query, &response)
		if err != nil {
			t.Fatalf("Failed to update task: %v", err)
		}
		var tasksResponse struct {
			Tasks []interface{} `json:"tasks"`
		}
		tc.client.Post(`query { tasks { id title content priority } }`, &tasksResponse)
		jsonBytes, _ := json.MarshalIndent(tasksResponse, "", "  ")
		snapshotResult(t, "tasks_after_update", string(jsonBytes))
	})
}

// TestIntegration_Delete tests delete operations
func TestIntegration_Delete(t *testing.T) {
	tc, tracker := setupTestClient(t)
	seed := testutil.GetSeedData()

	// Bootstrap first
	bootstrapData(t, tc, tracker, seed)

	// Delete a note
	t.Run("DeleteNote", func(t *testing.T) {
		noteID := tracker.Notes["Meeting notes - Sprint 1"]
		var response struct {
			DeleteNote bool `json:"deleteNote"`
		}
		query := fmt.Sprintf(`mutation { deleteNote(id: "%s") }`, noteID)
		err := tc.client.Post(query, &response)
		if err != nil {
			t.Fatalf("Failed to delete note: %v", err)
		}
		var notesResponse struct {
			Notes []interface{} `json:"notes"`
		}
		tc.client.Post(`query { notes { id title content } }`, &notesResponse)
		jsonBytes, _ := json.MarshalIndent(notesResponse, "", "  ")
		snapshotResult(t, "notes_after_delete", string(jsonBytes))
	})

	// Delete a task
	t.Run("DeleteTask", func(t *testing.T) {
		taskID := tracker.Tasks["Performance optimization"]
		var response struct {
			DeleteTask bool `json:"deleteTask"`
		}
		query := fmt.Sprintf(`mutation { deleteTask(id: "%s") }`, taskID)
		err := tc.client.Post(query, &response)
		if err != nil {
			t.Fatalf("Failed to delete task: %v", err)
		}
		var tasksResponse struct {
			Tasks []interface{} `json:"tasks"`
		}
		tc.client.Post(`query { tasks { id title content priority } }`, &tasksResponse)
		jsonBytes, _ := json.MarshalIndent(tasksResponse, "", "  ")
		snapshotResult(t, "tasks_after_delete", string(jsonBytes))
	})
}

// TestIntegration_Subscriptions tests subscription functionality
func TestIntegration_Subscriptions(t *testing.T) {
	db, err := testutil.SetupAndBootstrapTestDB()
	if err != nil {
		t.Fatalf("Failed to setup test database: %v", err)
	}

	resolver := NewResolver(db)
	schema := NewExecutableSchema(Config{Resolvers: resolver})
	srv := handler.NewDefaultServer(schema)
	authSrv := auth.AuthMiddleware(srv)

	// Create admin client
	adminClient := client.New(authSrv)

	// Login as admin
	var loginResponse struct {
		Login struct {
			Token string `json:"token"`
			User  struct {
				ID    string `json:"id"`
				Email string `json:"email"`
			} `json:"user"`
		} `json:"login"`
	}
	loginQuery := `mutation { login(email: "admin@example.com", password: "secret123") { token user { id email } } }`
	err = adminClient.Post(loginQuery, &loginResponse)
	if err != nil {
		t.Fatalf("Failed to login as admin: %v", err)
	}
	adminToken := loginResponse.Login.Token
	adminID := loginResponse.Login.User.ID
	adminClient = client.New(authSrv, client.AddHeader("Authorization", "Bearer "+adminToken))

	// Create user1
	var createUserResponse struct {
		CreateUser struct {
			ID    string `json:"id"`
			Email string `json:"email"`
		} `json:"createUser"`
	}

	// Get user role ID
	var rolesResponse struct {
		Roles []struct {
			ID   string `json:"id"`
			Name string `json:"name"`
		} `json:"roles"`
	}
	adminClient.Post(`query { roles { id name } }`, &rolesResponse)
	var userRoleID string
	for _, role := range rolesResponse.Roles {
		if role.Name == "user" {
			userRoleID = role.ID
			break
		}
	}

	createUserQuery := fmt.Sprintf(`mutation { createUser(input: {email: "user1@example.com", password: "password123", roles: ["%s"]}) { id email } }`, userRoleID)
	err = adminClient.Post(createUserQuery, &createUserResponse)
	if err != nil {
		t.Fatalf("Failed to create user1: %v", err)
	}
	user1ID := createUserResponse.CreateUser.ID

	// Login as user1
	var user1LoginResponse struct {
		Login struct {
			Token string `json:"token"`
		} `json:"login"`
	}
	user1LoginQuery := `mutation { login(email: "user1@example.com", password: "password123") { token } }`
	err = adminClient.Post(user1LoginQuery, &user1LoginResponse)
	if err != nil {
		t.Fatalf("Failed to login as user1: %v", err)
	}
	user1Token := user1LoginResponse.Login.Token

	// Create WebSocket client for user1
	user1WSClient := client.New(authSrv, client.AddHeader("Authorization", "Bearer "+user1Token))

	// Subscribe to taskCreated as user1
	taskCreatedSub := `subscription { taskCreated { id title content assigneeId } }`
	taskCreatedChan := make(chan *model.Task, 1)
	taskCreatedSubscription := user1WSClient.Websocket(taskCreatedSub)
	defer taskCreatedSubscription.Close()
	go func() {
		for {
			var taskCreatedResponse struct {
				TaskCreated *model.Task `json:"taskCreated"`
			}
			err := taskCreatedSubscription.Next(&taskCreatedResponse)
			if err != nil {
				return
			}
			if taskCreatedResponse.TaskCreated != nil {
				select {
				case taskCreatedChan <- taskCreatedResponse.TaskCreated:
				default:
				}
			}
		}
	}()

	// Subscribe to messageAdded as user1
	messageAddedSub := `subscription { messageAdded { id content senderId receivers } }`
	messageAddedChan := make(chan *model.Message, 1)
	messageAddedSubscription := user1WSClient.Websocket(messageAddedSub)
	defer messageAddedSubscription.Close()
	go func() {
		for {
			var messageAddedResponse struct {
				MessageAdded *model.Message `json:"messageAdded"`
			}
			err := messageAddedSubscription.Next(&messageAddedResponse)
			if err != nil {
				return
			}
			if messageAddedResponse.MessageAdded != nil {
				select {
				case messageAddedChan <- messageAddedResponse.MessageAdded:
				default:
				}
			}
		}
	}()

	// Give subscriptions time to connect
	time.Sleep(100 * time.Millisecond)

	// Admin sends a message to user1
	var createMessageResponse struct {
		CreateMessage struct {
			ID string `json:"id"`
		} `json:"createMessage"`
	}
	createMessageQuery := fmt.Sprintf(`mutation { createMessage(input: {content: "Hello user1!", receivers: ["%s"]}) { id } }`, user1ID)
	err = adminClient.Post(createMessageQuery, &createMessageResponse)
	if err != nil {
		t.Fatalf("Failed to create message: %v", err)
	}

	// Wait for messageAdded event
	select {
	case msg := <-messageAddedChan:
		if msg == nil {
			t.Error("Expected messageAdded event, got nil")
		} else if msg.Content != "Hello user1!" {
			t.Errorf("Expected message content 'Hello user1!', got '%s'", msg.Content)
		} else {
			t.Log("user1 received messageAdded event successfully")
		}
	case <-time.After(2 * time.Second):
		t.Error("Timeout waiting for messageAdded event")
	}

	// Get a task status ID
	var statusesResponse struct {
		TaskStatuses []struct {
			ID   string `json:"id"`
			Code string `json:"code"`
		} `json:"taskStatuses"`
	}
	adminClient.Post(`query { taskStatuses { id code } }`, &statusesResponse)
	var statusID string
	if len(statusesResponse.TaskStatuses) > 0 {
		statusID = statusesResponse.TaskStatuses[0].ID
	}

	// Admin creates a task assigned to user1
	var createTaskResponse struct {
		CreateTask struct {
			ID    string `json:"id"`
			Title string `json:"title"`
		} `json:"createTask"`
	}
	createTaskQuery := fmt.Sprintf(`mutation { createTask(input: {title: "Task for user1", content: "This is assigned to user1", createdById: "%s", assigneeId: "%s", statusId: "%s", priority: "medium"}) { id title } }`, adminID, user1ID, statusID)
	err = adminClient.Post(createTaskQuery, &createTaskResponse)
	if err != nil {
		t.Fatalf("Failed to create task: %v", err)
	}

	// Wait for taskCreated event
	select {
	case task := <-taskCreatedChan:
		if task == nil {
			t.Error("Expected taskCreated event, got nil")
		} else if task.Title != "Task for user1" {
			t.Errorf("Expected task title 'Task for user1', got '%s'", task.Title)
		} else {
			t.Log("user1 received taskCreated event successfully")
		}
	case <-time.After(2 * time.Second):
		t.Error("Timeout waiting for taskCreated event")
	}
}

// bootstrapData creates all entities for testing (skips existing items)
func bootstrapData(t *testing.T, tc *TestClient, tracker *IDTracker, seed testutil.SeedData) {
	// Create Permissions (skip if already exists)
	for _, perm := range seed.Permissions {
		if _, exists := tracker.Permissions[perm.Code]; exists {
			continue
		}
		var response struct {
			CreatePermission struct {
				ID string `json:"id"`
			} `json:"createPermission"`
		}
		query := fmt.Sprintf(`mutation { createPermission(input: {code: "%s", description: "%s"}) { id } }`, perm.Code, perm.Description)
		err := tc.client.Post(query, &response)
		if err != nil {
			t.Fatalf("Failed to create permission %s: %v", perm.Code, err)
		}
		tracker.Permissions[perm.Code] = response.CreatePermission.ID
	}

	// Create Roles (skip if already exists)
	for _, role := range seed.Roles {
		if _, exists := tracker.Roles[role.Name]; exists {
			continue
		}
		permIDs := make([]string, len(role.PermissionCodes))
		for i, code := range role.PermissionCodes {
			permIDs[i] = tracker.Permissions[code]
		}
		var response struct {
			CreateRole struct {
				ID string `json:"id"`
			} `json:"createRole"`
		}
		query := fmt.Sprintf(`mutation { createRole(input: {name: "%s", description: "%s", permissions: ["%s"]}) { id } }`, role.Name, role.Description, strings.Join(permIDs, `", "`))
		err := tc.client.Post(query, &response)
		if err != nil {
			t.Fatalf("Failed to create role %s: %v", role.Name, err)
		}
		tracker.Roles[role.Name] = response.CreateRole.ID
	}

	// Create Users (skip if already exists)
	for _, user := range seed.Users {
		if _, exists := tracker.Users[user.Email]; exists {
			continue
		}
		roleIDs := make([]string, len(user.RoleNames))
		for i, name := range user.RoleNames {
			roleIDs[i] = tracker.Roles[name]
		}
		var response struct {
			CreateUser struct {
				ID string `json:"id"`
			} `json:"createUser"`
		}
		query := fmt.Sprintf(`mutation { createUser(input: {email: "%s", password: "%s", roles: ["%s"]}) { id } }`, user.Email, user.Password, strings.Join(roleIDs, `", "`))
		err := tc.client.Post(query, &response)
		if err != nil {
			t.Fatalf("Failed to create user %s: %v", user.Email, err)
		}
		tracker.Users[user.Email] = response.CreateUser.ID
	}

	// Create Task Statuses (skip if already exists)
	for _, status := range seed.TaskStatuses {
		if _, exists := tracker.TaskStatuses[status.Code]; exists {
			continue
		}
		var response struct {
			CreateTaskStatus struct {
				ID string `json:"id"`
			} `json:"createTaskStatus"`
		}
		query := fmt.Sprintf(`mutation { createTaskStatus(input: {code: "%s", label: "%s"}) { id } }`, status.Code, status.Label)
		err := tc.client.Post(query, &response)
		if err != nil {
			t.Fatalf("Failed to create task status %s: %v", status.Code, err)
		}
		tracker.TaskStatuses[status.Code] = response.CreateTaskStatus.ID
	}

	// Create Services
	for _, service := range seed.Services {
		participantIDs := make([]string, len(service.ParticipantEmails))
		for i, email := range service.ParticipantEmails {
			participantIDs[i] = tracker.Users[email]
		}
		var response struct {
			CreateService struct {
				ID string `json:"id"`
			} `json:"createService"`
		}
		query := fmt.Sprintf(`mutation { createService(input: {name: "%s", description: "%s", createdById: "%s", participants: ["%s"]}) { id } }`, service.Name, service.Description, tracker.Users[service.CreatorEmail], strings.Join(participantIDs, `", "`))
		err := tc.client.Post(query, &response)
		if err != nil {
			t.Fatalf("Failed to create service %s: %v", service.Name, err)
		}
		tracker.Services[service.Name] = response.CreateService.ID
	}

	// Create Tasks
	for _, task := range seed.Tasks {
		var response struct {
			CreateTask struct {
				ID string `json:"id"`
			} `json:"createTask"`
		}
		statusID := tracker.TaskStatuses[task.StatusCode]
		if task.AssigneeEmail != "" {
			query := fmt.Sprintf(`mutation { createTask(input: {title: "%s", content: "%s", createdById: "%s", assigneeId: "%s", statusId: "%s", priority: "%s"}) { id } }`, task.Title, task.Content, tracker.Users[task.CreatorEmail], tracker.Users[task.AssigneeEmail], statusID, task.Priority)
			err := tc.client.Post(query, &response)
			if err != nil {
				t.Fatalf("Failed to create task %s: %v", task.Title, err)
			}
		} else {
			query := fmt.Sprintf(`mutation { createTask(input: {title: "%s", content: "%s", createdById: "%s", statusId: "%s", priority: "%s"}) { id } }`, task.Title, task.Content, tracker.Users[task.CreatorEmail], statusID, task.Priority)
			err := tc.client.Post(query, &response)
			if err != nil {
				t.Fatalf("Failed to create task %s: %v", task.Title, err)
			}
		}
		tracker.Tasks[task.Title] = response.CreateTask.ID
	}

	// Create Notes
	for _, note := range seed.Notes {
		var response struct {
			CreateNote struct {
				ID string `json:"id"`
			} `json:"createNote"`
		}
		query := fmt.Sprintf(`mutation { createNote(input: {title: "%s", content: "%s", userId: "%s", serviceId: "%s"}) { id } }`, note.Title, note.Content, tracker.Users[note.UserEmail], tracker.Services[note.ServiceName])
		err := tc.client.Post(query, &response)
		if err != nil {
			t.Fatalf("Failed to create note %s: %v", note.Title, err)
		}
		tracker.Notes[note.Title] = response.CreateNote.ID
	}

	// Create Messages
	for _, msg := range seed.Messages {
		receiverIDs := make([]string, len(msg.ReceiverEmails))
		for i, email := range msg.ReceiverEmails {
			receiverIDs[i] = tracker.Users[email]
		}
		var response struct {
			CreateMessage struct {
				ID string `json:"id"`
			} `json:"createMessage"`
		}
		query := fmt.Sprintf(`mutation { createMessage(input: {content: "%s", receivers: ["%s"]}) { id } }`, msg.Content, strings.Join(receiverIDs, `", "`))
		err := tc.client.Post(query, &response)
		if err != nil {
			t.Fatalf("Failed to create message: %v", err)
		}
		tracker.Messages = append(tracker.Messages, response.CreateMessage.ID)
	}
}
