/**
 * Projects Status & Presence
 *
 * Real-time presence tracking:
 * - projects:get-status  (HTTP) - Get project status
 * - projects:join        (Event) - User joins a project
 * - projects:leave       (Event) - User leaves a project
 * - projects:presence-updated (Broadcast) - Full presence state
 *
 * Single broadcast event: `projects:presence-updated` contains full state.
 */

import { t } from 'elysia';
import { createRouter } from '$shared/utils/ws-server';
import { getProjectStatusData, updateUserPresence } from '../../project/status-manager';
import { streamManager } from '../../chat/stream-manager';
import { ws } from '$backend/utils/ws';
import { debug } from '$shared/utils/logger';
import { requireProjectAccess } from '../access';

/**
 * Broadcast full presence state of all projects to all connected clients
 */
export async function broadcastPresence() {
	const statuses = await getProjectStatusData();
	ws.emit.global('projects:presence-updated', {
		type: 'presence-updated',
		timestamp: Date.now(),
		data: statuses
	});
}

export const statusHandler = createRouter()
	// Get project status (HTTP)
	.http('projects:get-status', {
		data: t.Object({}),
		response: t.Any()
	}, async ({ conn }) => {
		const projectId = ws.getProjectId(conn);
		return await getProjectStatusData(projectId);
	})

	// User joins a project
	.on('projects:join', {
		data: t.Object({
			userName: t.Optional(t.String())
		})
	}, async ({ data, conn }) => {
		const projectId = ws.getProjectId(conn);
		const userId = ws.getUserId(conn);

		try {
			// Send initial presence for ALL projects
			const allStatuses = await getProjectStatusData();
			ws.emit.user(userId, 'projects:presence-updated', {
				type: 'presence-updated',
				timestamp: Date.now(),
				data: allStatuses
			});

			if (data.userName) {
				updateUserPresence(projectId, userId, data.userName, 'join');

				try {
					streamManager.cleanupProjectStreams(projectId);
				} catch (cleanupError) {
					debug.error('project', 'Error cleaning up project streams:', cleanupError);
				}

				// Auto-cleanup on connection close (tab close, network drop)
				ws.addCleanup(conn, () => {
					updateUserPresence(projectId, userId, '', 'leave');
					debug.log('project', `Auto-cleaned presence for ${userId} from project ${projectId}`);
					broadcastPresence().catch((err) => {
						debug.warn('project', `Presence broadcast error after auto-cleanup for ${userId}:`, err);
					});
				});

				await broadcastPresence();
			}

			debug.log('project', `User ${userId} joined project ${projectId}`);
		} catch (error) {
			debug.error('project', 'Error joining project:', error);
			ws.emit.user(userId, 'projects:error', {
				error: error instanceof Error ? error.message : 'Failed to join project'
			});
		}
	})

	// User leaves a project
	.on('projects:leave', {
		data: t.Object({
			projectId: t.Optional(t.String())
		})
	}, async ({ data, conn }) => {
		// Use explicit projectId from client (avoids race condition when switching projects)
		const projectId = data.projectId || ws.getProjectId(conn);
		const userId = ws.getUserId(conn);
		requireProjectAccess(conn, projectId);

		try {
			updateUserPresence(projectId, userId, '', 'leave');
			await broadcastPresence();
			debug.log('project', `User ${userId} left project ${projectId}`);
		} catch (error) {
			debug.error('project', 'Error leaving project:', error);
		}
	})

	// Event declarations
	.emit('projects:presence-updated', t.Object({
		type: t.Literal('presence-updated'),
		timestamp: t.Number(),
		data: t.Any()
	}))

	.emit('projects:error', t.Object({
		error: t.String()
	}));
