import { json, type RequestHandler } from '@sveltejs/kit';
import {
	getEnvironments,
	getLatestHostMetrics,
	getContainerEventStats,
	getEnvSetting,
	hasEnvironments,
	getEnvUpdateCheckSettings
} from '$lib/server/db';
import {
	listContainers,
	listImages,
	listVolumes,
	listNetworks,
	getDockerInfo,
	getDiskUsage
} from '$lib/server/docker';
import { listComposeStacks } from '$lib/server/stacks';
import { authorize } from '$lib/server/authorize';
import { parseLabels } from '$lib/utils/label-colors';

// Helper to add timeout to promises
function withTimeout<T>(promise: Promise<T>, ms: number, fallback: T): Promise<T> {
	return Promise.race([
		promise,
		new Promise<T>((resolve) => setTimeout(() => resolve(fallback), ms))
	]);
}

// Loading states for progressive tile updates
export interface LoadingStates {
	containers?: boolean;
	images?: boolean;
	volumes?: boolean;
	networks?: boolean;
	stacks?: boolean;
	diskUsage?: boolean;
	topContainers?: boolean;
}

export interface EnvironmentStats {
	id: number;
	name: string;
	host?: string;
	port?: number | null;
	icon: string;
	socketPath?: string;
	collectActivity: boolean;
	collectMetrics: boolean;
	scannerEnabled: boolean;
	updateCheckEnabled: boolean;
	updateCheckAutoUpdate: boolean;
	labels?: string[];
	connectionType: 'socket' | 'direct' | 'hawser-standard' | 'hawser-edge';
	online: boolean;
	error?: string;
	containers: {
		total: number;
		running: number;
		stopped: number;
		paused: number;
		restarting: number;
		unhealthy: number;
	};
	images: {
		total: number;
		totalSize: number;
	};
	volumes: {
		total: number;
		totalSize: number;
	};
	containersSize: number;
	buildCacheSize: number;
	networks: {
		total: number;
	};
	stacks: {
		total: number;
		running: number;
		partial: number;
		stopped: number;
	};
	metrics: {
		cpuPercent: number;
		memoryPercent: number;
		memoryUsed: number;
		memoryTotal: number;
	} | null;
	events: {
		total: number;
		today: number;
	};
	topContainers: Array<{
		id: string;
		name: string;
		cpuPercent: number;
		memoryPercent: number;
	}>;
	metricsHistory?: Array<{
		cpu_percent: number;
		memory_percent: number;
		timestamp: string;
	}>;
	recentEvents?: Array<{
		container_name: string;
		action: string;
		timestamp: string;
	}>;
	// Progressive loading states
	loading?: LoadingStates;
}

export const GET: RequestHandler = async ({ cookies, url }) => {
	const auth = await authorize(cookies);

	// Support single environment query for real-time updates
	const envIdParam = url.searchParams.get('env');
	const envIdNum = envIdParam ? parseInt(envIdParam) : undefined;

	// Permission check with environment context
	if (auth.authEnabled && !await auth.can('environments', 'view', envIdNum)) {
		return json({ error: 'Permission denied' }, { status: 403 });
	}

	// Early return if no environments configured (fresh install)
	if (!await hasEnvironments()) {
		return json([]);
	}

	try {
		let environments = await getEnvironments();

		// Filter to single environment if specified
		if (envIdNum) {
			environments = environments.filter(env => env.id === envIdNum);
			if (environments.length === 0) {
				return json({ error: 'Environment not found' }, { status: 404 });
			}
		}

		// In enterprise mode, filter environments by user's accessible environments
		if (auth.authEnabled && auth.isEnterprise && auth.isAuthenticated && !auth.isAdmin) {
			const accessibleIds = await auth.getAccessibleEnvironmentIds();
			// accessibleIds is null if user has access to all environments
			if (accessibleIds !== null) {
				environments = environments.filter(env => accessibleIds.includes(env.id));
			}
		}

		// Fetch stats for all environments in parallel
		const promises = environments.map(async (env): Promise<EnvironmentStats> => {
			const envStats: EnvironmentStats = {
				id: env.id,
				name: env.name,
				host: env.host ?? undefined,
				port: env.port ?? undefined,
				icon: env.icon || 'globe',
				socketPath: env.socketPath ?? undefined,
				collectActivity: env.collectActivity,
				collectMetrics: env.collectMetrics ?? true,
				scannerEnabled: false,
				updateCheckEnabled: false,
				updateCheckAutoUpdate: false,
				labels: parseLabels(env.labels),
				connectionType: (env.connectionType as 'socket' | 'direct' | 'hawser-standard' | 'hawser-edge') || 'socket',
				online: false,
				containers: { total: 0, running: 0, stopped: 0, paused: 0, restarting: 0, unhealthy: 0 },
				images: { total: 0, totalSize: 0 },
				volumes: { total: 0, totalSize: 0 },
				containersSize: 0,
				buildCacheSize: 0,
				networks: { total: 0 },
				stacks: { total: 0, running: 0, partial: 0, stopped: 0 },
				metrics: null,
				events: { total: 0, today: 0 },
				topContainers: []
			};

			try {
				// Check scanner settings - scanner type is stored in 'vulnerability_scanner'
				const scannerType = await getEnvSetting('vulnerability_scanner', env.id);
				envStats.scannerEnabled = scannerType && scannerType !== 'none';

				// Check update check settings
				const updateCheckSettings = await getEnvUpdateCheckSettings(env.id);
				if (updateCheckSettings && updateCheckSettings.enabled) {
					envStats.updateCheckEnabled = true;
					envStats.updateCheckAutoUpdate = updateCheckSettings.autoUpdate;
				}

				// Check if Docker is accessible (with 5 second timeout)
				const dockerInfo = await withTimeout(getDockerInfo(env.id), 5000, null);
				if (!dockerInfo) {
					envStats.error = 'Connection timeout or Docker not accessible';
					return envStats;
				}
				envStats.online = true;

				// Fetch all data in parallel (with 10 second timeout per operation)
				const [containers, images, volumes, networks, stacks, diskUsage] = await Promise.all([
					withTimeout(listContainers(true, env.id).catch(() => []), 10000, []),
					withTimeout(listImages(env.id).catch(() => []), 10000, []),
					withTimeout(listVolumes(env.id).catch(() => []), 10000, []),
					withTimeout(listNetworks(env.id).catch(() => []), 10000, []),
					withTimeout(listComposeStacks(env.id).catch(() => []), 10000, []),
					withTimeout(getDiskUsage(env.id).catch(() => null), 10000, null)
				]);

				// Process containers
				envStats.containers.total = containers.length;
				envStats.containers.running = containers.filter((c: any) => c.state === 'running').length;
				envStats.containers.stopped = containers.filter((c: any) => c.state === 'exited').length;
				envStats.containers.paused = containers.filter((c: any) => c.state === 'paused').length;
				envStats.containers.restarting = containers.filter((c: any) => c.state === 'restarting').length;
				envStats.containers.unhealthy = containers.filter((c: any) => c.health === 'unhealthy').length;

				// Helper to get valid size (Docker API returns -1 for uncalculated sizes)
				const getValidSize = (size: number | undefined | null): number => {
					return size && size > 0 ? size : 0;
				};

				// Process disk usage from /system/df for accurate size data
				if (diskUsage) {
					// Images: use Size from /system/df
					envStats.images.total = diskUsage.Images?.length || images.length;
					envStats.images.totalSize = diskUsage.Images?.reduce((sum: number, img: any) => sum + getValidSize(img.Size), 0) || 0;

					// Volumes: use UsageData.Size from /system/df
					envStats.volumes.total = diskUsage.Volumes?.length || volumes.length;
					envStats.volumes.totalSize = diskUsage.Volumes?.reduce((sum: number, vol: any) => sum + getValidSize(vol.UsageData?.Size), 0) || 0;

					// Containers: use SizeRw (writable layer size)
					envStats.containersSize = diskUsage.Containers?.reduce((sum: number, c: any) => sum + getValidSize(c.SizeRw), 0) || 0;

					// Build cache: total size
					envStats.buildCacheSize = diskUsage.BuildCache?.reduce((sum: number, bc: any) => sum + getValidSize(bc.Size), 0) || 0;
				} else {
					// Fallback to original method if /system/df failed
					envStats.images.total = images.length;
					envStats.images.totalSize = images.reduce((sum: number, img: any) => sum + getValidSize(img.size), 0);
					envStats.volumes.total = volumes.length;
					envStats.volumes.totalSize = 0;
				}

				// Process networks
				envStats.networks.total = networks.length;

				// Process stacks
				envStats.stacks.total = stacks.length;
				envStats.stacks.running = stacks.filter((s: any) => s.status === 'running').length;
				envStats.stacks.partial = stacks.filter((s: any) => s.status === 'partial').length;
				envStats.stacks.stopped = stacks.filter((s: any) => s.status === 'stopped').length;

				// Get latest metrics and event stats in parallel
				const [latestMetrics, eventStats] = await Promise.all([
					getLatestHostMetrics(env.id),
					getContainerEventStats(env.id)
				]);

				if (latestMetrics) {
					envStats.metrics = {
						cpuPercent: latestMetrics.cpuPercent,
						memoryPercent: latestMetrics.memoryPercent,
						memoryUsed: latestMetrics.memoryUsed,
						memoryTotal: latestMetrics.memoryTotal
					};
				}

				envStats.events = {
					total: eventStats.total,
					today: eventStats.today
				};

			} catch (error) {
				// Convert technical error messages to user-friendly ones
				const errorStr = String(error);
				if (errorStr.includes('FailedToOpenSocket') || errorStr.includes('ECONNREFUSED')) {
					envStats.error = 'Docker socket not accessible';
				} else if (errorStr.includes('ECONNRESET') || errorStr.includes('connection was closed')) {
					envStats.error = 'Connection lost';
				} else if (errorStr.includes('verbose: true') || errorStr.includes('verbose')) {
					envStats.error = 'Connection failed';
				} else if (errorStr.includes('timeout') || errorStr.includes('Timeout')) {
					envStats.error = 'Connection timeout';
				} else {
					const match = errorStr.match(/^(?:Error:\s*)?([^.!?]+[.!?]?)/);
					envStats.error = match ? match[1].trim() : 'Connection error';
				}
			}

			return envStats;
		});

		const results = await Promise.all(promises);

		// Return single object if single env was requested
		if (envIdParam && results.length === 1) {
			return json(results[0]);
		}

		return json(results);
	} catch (error: any) {
		console.error('Failed to get dashboard stats:', error);
		return json({ error: 'Failed to get dashboard stats' }, { status: 500 });
	}
};
