import {readFileSync} from 'node:fs';
import path from 'node:path';
import {RenderInternals} from '@remotion/renderer';
import type {
	SaveSequencePropsRequest,
	SaveSequencePropsResponse,
} from '@remotion/studio-shared';
import {updateSequenceProps} from '../../codemods/update-sequence-props';
import {writeFileAndNotifyFileWatchers} from '../../file-watcher';
import type {ApiHandler} from '../api-types';
import {
	printUndoHint,
	pushToUndoStack,
	suppressUndoStackInvalidation,
} from '../undo-stack';
import {suppressBundlerUpdateForFile} from '../watch-ignore-next-change';
import {computeSequencePropsStatus} from './can-update-sequence-props';
import {formatPropChange, logUpdate, normalizeQuotes} from './log-update';

export const saveSequencePropsHandler: ApiHandler<
	SaveSequencePropsRequest,
	SaveSequencePropsResponse
> = async ({
	input: {fileName, nodePath, key, value, defaultValue, observedKeys},
	remotionRoot,
	logLevel,
}) => {
	try {
		RenderInternals.Log.trace(
			{indent: false, logLevel},
			`[save-sequence-props] Received request for fileName="${fileName}" key="${key}"`,
		);
		const absolutePath = path.resolve(remotionRoot, fileName);
		const fileRelativeToRoot = path.relative(remotionRoot, absolutePath);
		if (fileRelativeToRoot.startsWith('..')) {
			throw new Error('Cannot modify a file outside the project');
		}

		const fileContents = readFileSync(absolutePath, 'utf-8');

		const {output, oldValueString, formatted} = await updateSequenceProps({
			input: fileContents,
			nodePath,
			key,
			value: JSON.parse(value),
			defaultValue: defaultValue !== null ? JSON.parse(defaultValue) : null,
		});

		const newValueString = JSON.stringify(JSON.parse(value));
		const parsedDefault =
			defaultValue !== null ? JSON.parse(defaultValue) : null;
		const defaultValueString =
			parsedDefault !== null ? JSON.stringify(parsedDefault) : null;

		const normalizedOld = normalizeQuotes(oldValueString);
		const normalizedNew = normalizeQuotes(newValueString);
		const normalizedDefault =
			defaultValueString !== null ? normalizeQuotes(defaultValueString) : null;

		const undoPropChange = formatPropChange({
			key,
			oldValueString: normalizedNew,
			newValueString: normalizedOld,
			defaultValueString: normalizedDefault,
		});
		const redoPropChange = formatPropChange({
			key,
			oldValueString: normalizedOld,
			newValueString: normalizedNew,
			defaultValueString: normalizedDefault,
		});

		pushToUndoStack({
			filePath: absolutePath,
			oldContents: fileContents,
			logLevel,
			remotionRoot,
			description: {
				undoMessage: `Undid ${undoPropChange}`,
				redoMessage: `Redid ${redoPropChange}`,
			},
			entryType: 'sequence-props',
		});
		suppressUndoStackInvalidation(absolutePath);
		suppressBundlerUpdateForFile(absolutePath);
		writeFileAndNotifyFileWatchers(absolutePath, output);

		logUpdate({
			absolutePath,
			fileRelativeToRoot,
			key,
			oldValueString,
			newValueString,
			defaultValueString,
			formatted,
			logLevel,
		});

		printUndoHint(logLevel);

		const newStatus = computeSequencePropsStatus({
			fileName,
			keys: observedKeys,
			nodePath,
			remotionRoot,
		});

		return {
			success: true,
			newStatus,
		};
	} catch (err) {
		return {
			success: false,
			reason: (err as Error).message,
			stack: (err as Error).stack as string,
		};
	}
};
