import { json } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
import { authorize } from '$lib/server/authorize';
import {
	listContainers,
	inspectContainer,
	stopContainer,
	removeContainer,
	createContainer,
	pullImage,
	getTempImageTag,
	isDigestBasedImage,
	getImageIdByTag,
	removeTempImage,
	tagImage
} from '$lib/server/docker';
import { auditContainer } from '$lib/server/audit';
import { getScannerSettings, scanImage } from '$lib/server/scanner';
import { saveVulnerabilityScan, removePendingContainerUpdate, type VulnerabilityCriteria } from '$lib/server/db';
import { parseImageNameAndTag, shouldBlockUpdate, combineScanSummaries, isDockhandContainer } from '$lib/server/scheduler/tasks/update-utils';

export interface ScanResult {
	critical: number;
	high: number;
	medium: number;
	low: number;
	negligible?: number;
	unknown?: number;
}

export interface ScannerResult extends ScanResult {
	scanner: 'grype' | 'trivy';
}

export interface UpdateProgress {
	type: 'start' | 'progress' | 'pull_log' | 'scan_start' | 'scan_log' | 'scan_complete' | 'blocked' | 'complete' | 'error';
	containerId?: string;
	containerName?: string;
	step?: 'pulling' | 'scanning' | 'stopping' | 'removing' | 'creating' | 'starting' | 'done' | 'failed' | 'blocked' | 'skipped';
	message?: string;
	current?: number;
	total?: number;
	success?: boolean;
	error?: string;
	summary?: {
		total: number;
		success: number;
		failed: number;
		blocked: number;
		skipped: number;
	};
	// Pull log specific fields
	pullStatus?: string;
	pullId?: string;
	pullProgress?: string;
	// Scan specific fields
	scanResult?: ScanResult;
	scannerResults?: ScannerResult[];
	blockReason?: string;
	scanner?: string;
}

/**
 * Batch update containers with streaming progress.
 * Expects JSON body: { containerIds: string[], vulnerabilityCriteria?: VulnerabilityCriteria }
 */
export const POST: RequestHandler = async (event) => {
	const { url, cookies, request } = event;
	const auth = await authorize(cookies);

	const envId = url.searchParams.get('env');
	const envIdNum = envId ? parseInt(envId) : undefined;

	// Need create permission to recreate containers
	if (auth.authEnabled && !await auth.can('containers', 'create', envIdNum)) {
		return json({ error: 'Permission denied' }, { status: 403 });
	}

	let body: { containerIds: string[]; vulnerabilityCriteria?: VulnerabilityCriteria };
	try {
		body = await request.json();
	} catch {
		return json({ error: 'Invalid JSON body' }, { status: 400 });
	}

	const { containerIds, vulnerabilityCriteria = 'never' } = body;

	if (!containerIds || !Array.isArray(containerIds) || containerIds.length === 0) {
		return json({ error: 'containerIds array is required' }, { status: 400 });
	}

	const encoder = new TextEncoder();
	let controllerClosed = false;
	let keepaliveInterval: ReturnType<typeof setInterval> | null = null;

	const stream = new ReadableStream({
		async start(controller) {
			const safeEnqueue = (data: UpdateProgress) => {
				if (!controllerClosed) {
					try {
						controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\n\n`));
					} catch {
						controllerClosed = true;
					}
				}
			};

			// Send SSE keepalive comments every 5s to prevent Traefik (10s idle timeout) from closing connection
			keepaliveInterval = setInterval(() => {
				if (controllerClosed) return;
				try {
					controller.enqueue(encoder.encode(`: keepalive\n\n`));
				} catch {
					controllerClosed = true;
				}
			}, 5000);

			let successCount = 0;
			let failCount = 0;
			let blockedCount = 0;
			let skippedCount = 0;

			// Get scanner settings for this environment
			const scannerSettings = await getScannerSettings(envIdNum);
			// Scan if scanning is enabled (scanner !== 'none')
			// The vulnerabilityCriteria only controls whether to BLOCK updates, not whether to SCAN
			const shouldScan = scannerSettings.scanner !== 'none';

			// Send start event
			safeEnqueue({
				type: 'start',
				total: containerIds.length,
				message: `Starting update of ${containerIds.length} container${containerIds.length > 1 ? 's' : ''}${shouldScan ? ' with vulnerability scanning' : ''}`
			});

			// Process containers sequentially
			for (let i = 0; i < containerIds.length; i++) {
				const containerId = containerIds[i];
				let containerName = 'unknown';

				try {
					// Find container
					const containers = await listContainers(true, envIdNum);
					const container = containers.find(c => c.id === containerId);

					if (!container) {
						safeEnqueue({
							type: 'progress',
							containerId,
							containerName: 'unknown',
							step: 'failed',
							current: i + 1,
							total: containerIds.length,
							success: false,
							error: 'Container not found'
						});
						failCount++;
						continue;
					}

					containerName = container.name;

					// Get full container config
					const inspectData = await inspectContainer(containerId, envIdNum) as any;
					const wasRunning = inspectData.State.Running;
					const config = inspectData.Config;
					const hostConfig = inspectData.HostConfig;
					const imageName = config.Image;
					const currentImageId = inspectData.Image;

					// Skip Dockhand container - cannot update itself
					if (isDockhandContainer(imageName)) {
						safeEnqueue({
							type: 'progress',
							containerId,
							containerName,
							step: 'skipped',
							current: i + 1,
							total: containerIds.length,
							success: true,
							message: `Skipping ${containerName} - cannot update Dockhand itself`
						});
						skippedCount++;
						continue;
					}

					// Step 1: Pull latest image
					safeEnqueue({
						type: 'progress',
						containerId,
						containerName,
						step: 'pulling',
						current: i + 1,
						total: containerIds.length,
						message: `Pulling ${imageName}...`
					});

					try {
						await pullImage(imageName, (data: any) => {
							// Send pull progress as log entries
							if (data.status) {
								safeEnqueue({
									type: 'pull_log',
									containerId,
									containerName,
									pullStatus: data.status,
									pullId: data.id,
									pullProgress: data.progress
								});
							}
						}, envIdNum);
					} catch (pullError: any) {
						safeEnqueue({
							type: 'progress',
							containerId,
							containerName,
							step: 'failed',
							current: i + 1,
							total: containerIds.length,
							success: false,
							error: `Pull failed: ${pullError.message}`
						});
						failCount++;
						continue;
					}

					// SAFE-PULL FLOW with vulnerability scanning
					if (shouldScan && !isDigestBasedImage(imageName)) {
						const tempTag = getTempImageTag(imageName);

						// Get new image ID
						const newImageId = await getImageIdByTag(imageName, envIdNum);
						if (!newImageId) {
							safeEnqueue({
								type: 'progress',
								containerId,
								containerName,
								step: 'failed',
								current: i + 1,
								total: containerIds.length,
								success: false,
								error: 'Failed to get new image ID after pull'
							});
							failCount++;
							continue;
						}

						// Restore original tag to old image (safety)
						const [oldRepo, oldTag] = parseImageNameAndTag(imageName);
						try {
							await tagImage(currentImageId, oldRepo, oldTag, envIdNum);
						} catch {
							// Ignore - old image might have been removed
						}

						// Tag new image with temp suffix
						const [tempRepo, tempTagName] = parseImageNameAndTag(tempTag);
						await tagImage(newImageId, tempRepo, tempTagName, envIdNum);

						// Step 2: Scan temp image
						safeEnqueue({
							type: 'scan_start',
							containerId,
							containerName,
							step: 'scanning',
							current: i + 1,
							total: containerIds.length,
							message: `Scanning ${imageName} for vulnerabilities...`
						});

						let scanBlocked = false;
						let blockReason = '';
						let finalScanResult: ScanResult | undefined;
						let individualScannerResults: ScannerResult[] = [];

						try {
							const scanResults = await scanImage(tempTag, envIdNum, (progress) => {
								if (progress.message) {
									safeEnqueue({
										type: 'scan_log',
										containerId,
										containerName,
										scanner: progress.scanner,
										message: progress.message
									});
								}
							});

							if (scanResults.length > 0) {
								const scanSummary = combineScanSummaries(scanResults);
								finalScanResult = {
									critical: scanSummary.critical,
									high: scanSummary.high,
									medium: scanSummary.medium,
									low: scanSummary.low,
									negligible: scanSummary.negligible,
									unknown: scanSummary.unknown
								};

								// Build individual scanner results
								individualScannerResults = scanResults.map(result => ({
									scanner: result.scanner as 'grype' | 'trivy',
									critical: result.summary.critical,
									high: result.summary.high,
									medium: result.summary.medium,
									low: result.summary.low,
									negligible: result.summary.negligible,
									unknown: result.summary.unknown
								}));

								// Save scan results
								for (const result of scanResults) {
									try {
										await saveVulnerabilityScan({
											environmentId: envIdNum,
											imageId: newImageId,
											imageName: result.imageName,
											scanner: result.scanner,
											scannedAt: result.scannedAt,
											scanDuration: result.scanDuration,
											criticalCount: result.summary.critical,
											highCount: result.summary.high,
											mediumCount: result.summary.medium,
											lowCount: result.summary.low,
											negligibleCount: result.summary.negligible,
											unknownCount: result.summary.unknown,
											vulnerabilities: result.vulnerabilities,
											error: result.error ?? null
										});
									} catch { /* ignore save errors */ }
								}

								// Check if blocked
								const { blocked, reason } = shouldBlockUpdate(vulnerabilityCriteria, scanSummary, undefined);
								if (blocked) {
									scanBlocked = true;
									blockReason = reason;
								}
							}

							safeEnqueue({
								type: 'scan_complete',
								containerId,
								containerName,
								scanResult: finalScanResult,
								scannerResults: individualScannerResults.length > 0 ? individualScannerResults : undefined,
								message: finalScanResult
									? `Scan complete: ${finalScanResult.critical} critical, ${finalScanResult.high} high, ${finalScanResult.medium} medium, ${finalScanResult.low} low`
									: 'Scan complete: no vulnerabilities found'
							});

						} catch (scanErr: any) {
							safeEnqueue({
								type: 'progress',
								containerId,
								containerName,
								step: 'failed',
								current: i + 1,
								total: containerIds.length,
								success: false,
								error: `Scan failed: ${scanErr.message}`
							});

							// Clean up temp image on scan failure
							try {
								await removeTempImage(newImageId, envIdNum);
							} catch { /* ignore cleanup errors */ }

							failCount++;
							continue;
						}

						if (scanBlocked) {
							// BLOCKED - Remove temp image and skip this container
							safeEnqueue({
								type: 'blocked',
								containerId,
								containerName,
								step: 'blocked',
								current: i + 1,
								total: containerIds.length,
								success: false,
								scanResult: finalScanResult,
								scannerResults: individualScannerResults.length > 0 ? individualScannerResults : undefined,
								blockReason,
								message: `Update blocked: ${blockReason}`
							});

							try {
								await removeTempImage(newImageId, envIdNum);
							} catch { /* ignore cleanup errors */ }

							blockedCount++;
							continue;
						}

						// APPROVED - Re-tag to original
						await tagImage(newImageId, oldRepo, oldTag, envIdNum);
						try {
							await removeTempImage(tempTag, envIdNum);
						} catch { /* ignore cleanup errors */ }
					}

					// Step 3: Stop container if running
					if (wasRunning) {
						safeEnqueue({
							type: 'progress',
							containerId,
							containerName,
							step: 'stopping',
							current: i + 1,
							total: containerIds.length,
							message: `Stopping ${containerName}...`
						});
						await stopContainer(containerId, envIdNum);
					}

					// Step 4: Remove old container
					safeEnqueue({
						type: 'progress',
						containerId,
						containerName,
						step: 'removing',
						current: i + 1,
						total: containerIds.length,
						message: `Removing old container ${containerName}...`
					});
					await removeContainer(containerId, true, envIdNum);

					// Prepare port bindings
					const ports: { [key: string]: { HostPort: string } } = {};
					if (hostConfig.PortBindings) {
						for (const [containerPort, bindings] of Object.entries(hostConfig.PortBindings)) {
							if (bindings && (bindings as any[]).length > 0) {
								ports[containerPort] = { HostPort: (bindings as any[])[0].HostPort || '' };
							}
						}
					}

					// Step 5: Create new container
					safeEnqueue({
						type: 'progress',
						containerId,
						containerName,
						step: 'creating',
						current: i + 1,
						total: containerIds.length,
						message: `Creating new container ${containerName}...`
					});

					const newContainer = await createContainer({
						name: containerName,
						image: imageName,
						ports,
						volumeBinds: hostConfig.Binds || [],
						env: config.Env || [],
						labels: config.Labels || {},
						cmd: config.Cmd || undefined,
						restartPolicy: hostConfig.RestartPolicy?.Name || 'no',
						networkMode: hostConfig.NetworkMode || undefined
					}, envIdNum);

					// Step 6: Start if was running
					if (wasRunning) {
						safeEnqueue({
							type: 'progress',
							containerId,
							containerName,
							step: 'starting',
							current: i + 1,
							total: containerIds.length,
							message: `Starting ${containerName}...`
						});
						await newContainer.start();
					}

					// Audit log
					await auditContainer(event, 'update', newContainer.id, containerName, envIdNum, { batchUpdate: true });

					// Done with this container - use original containerId for UI consistency
					safeEnqueue({
						type: 'progress',
						containerId,
						containerName,
						step: 'done',
						current: i + 1,
						total: containerIds.length,
						success: true,
						message: `${containerName} updated successfully`
					});
					successCount++;

					// Clear pending update indicator from database
					if (envIdNum) {
						await removePendingContainerUpdate(envIdNum, containerId).catch(() => {
							// Ignore errors - record may not exist
						});
					}

				} catch (error: any) {
					safeEnqueue({
						type: 'progress',
						containerId,
						containerName,
						step: 'failed',
						current: i + 1,
						total: containerIds.length,
						success: false,
						error: error.message
					});
					failCount++;
				}
			}

			// Send complete event
			safeEnqueue({
				type: 'complete',
				summary: {
					total: containerIds.length,
					success: successCount,
					failed: failCount,
					blocked: blockedCount,
					skipped: skippedCount
				},
				message: skippedCount > 0 || blockedCount > 0
					? `Updated ${successCount} of ${containerIds.length} containers${blockedCount > 0 ? ` (${blockedCount} blocked)` : ''}${skippedCount > 0 ? ` (${skippedCount} skipped)` : ''}`
					: `Updated ${successCount} of ${containerIds.length} containers`
			});

			clearInterval(keepaliveInterval);
			controller.close();
		},
		cancel() {
			controllerClosed = true;
			if (keepaliveInterval) {
				clearInterval(keepaliveInterval);
			}
		}
	});

	return new Response(stream, {
		headers: {
			'Content-Type': 'text/event-stream',
			'Cache-Control': 'no-cache',
			'Connection': 'keep-alive'
		}
	});
};
