import { data, redirect } from 'react-router';
import { Capabilities } from '~/server/web/roles';
import type { Route } from './+types/machine';

export async function machineAction({ request, context }: Route.ActionArgs) {
	const session = await context.sessions.auth(request);
	const check = await context.sessions.check(
		request,
		Capabilities.write_machines,
	);

	const formData = await request.formData();
	const api = context.hsApi.getRuntimeClient(session.api_key);

	const action = formData.get('action_id')?.toString();
	if (!action) {
		throw data('Missing `action_id` in the form data.', {
			status: 400,
		});
	}

	// Fast track register since it doesn't require an existing machine
	if (action === 'register') {
		if (!check) {
			throw data('You do not have permission to manage machines', {
				status: 403,
			});
		}

		const registrationKey = formData.get('register_key')?.toString();
		if (!registrationKey) {
			throw data('Missing `register_key` in the form data.', {
				status: 400,
			});
		}

		const user = formData.get('user')?.toString();
		if (!user) {
			throw data('Missing `user` in the form data.', {
				status: 400,
			});
		}

		const node = await api.registerNode(user, registrationKey);
		return redirect(`/machines/${node.id}`);
	}

	// Check if the user has permission to manage this machine
	const nodeId = formData.get('node_id')?.toString();
	if (!nodeId) {
		throw data('Missing `node_id` in the form data.', {
			status: 400,
		});
	}

	const node = await api.getNode(nodeId);
	if (!node) {
		throw data(`Machine with ID ${nodeId} not found`, {
			status: 404,
		});
	}

	if (
		node.user.providerId?.split('/').pop() !== session.user.subject &&
		!check
	) {
		throw data('You do not have permission to act on this machine', {
			status: 403,
		});
	}

	switch (action) {
		case 'rename': {
			const newName = formData.get('name')?.toString();
			if (!newName) {
				throw data('Missing `name` in the form data.', {
					status: 400,
				});
			}

			const name = String(formData.get('name'));
			await api.renameNode(nodeId, name);
			return { message: 'Machine renamed' };
		}

		case 'delete': {
			await api.deleteNode(nodeId);
			return redirect('/machines');
		}

		case 'expire': {
			await api.expireNode(nodeId);
			return { message: 'Machine expired' };
		}

		case 'update_tags': {
			const tags = formData.get('tags')?.toString().split(',') ?? [];
			if (tags.length === 0) {
				throw data('Missing `tags` in the form data.', {
					status: 400,
				});
			}

			await api.setNodeTags(
				nodeId,
				tags.map((tag) => tag.trim()).filter((tag) => tag !== ''),
			);
			return { message: 'Tags updated' };
		}

		case 'update_routes': {
			const newApproved = node.approvedRoutes;
			const routes = formData.get('routes')?.toString();
			if (!routes) {
				throw data('Missing `routes` in the form data.', {
					status: 400,
				});
			}

			const allRoutes = routes.split(',').map((route) => route.trim());
			if (allRoutes.length === 0) {
				throw data('No routes provided to update', {
					status: 400,
				});
			}

			const enabled = formData.get('enabled')?.toString();
			if (enabled === undefined) {
				throw data('Missing `enabled` in the form data.', {
					status: 400,
				});
			}

			if (enabled === 'true') {
				for (const route of allRoutes) {
					// If already approved, skip, otherwise add to approved
					if (newApproved.includes(route)) {
						continue;
					}

					newApproved.push(route);
				}
			} else {
				for (const route of allRoutes) {
					// If not approved, skip, otherwise remove from approved
					if (!newApproved.includes(route)) {
						continue;
					}

					const index = newApproved.indexOf(route);
					if (index > -1) {
						newApproved.splice(index, 1);
					}
				}
			}

			await api.approveNodeRoutes(nodeId, newApproved);
			return { message: 'Routes updated' };
		}

		case 'reassign': {
			const user = formData.get('user_id')?.toString();
			if (!user) {
				throw data('Missing `user_id` in the form data.', {
					status: 400,
				});
			}

			await api.setNodeUser(nodeId, user);
			return { message: 'Machine reassigned' };
		}

		default:
			throw data('Invalid action', {
				status: 400,
			});
	}
}
