import { EditorCore } from "@/core";
import { Command, type CommandResult } from "@/commands/base-command";
import {
	getCustomMaskClosedStateAfterPointRemoval,
	removeCustomMaskPoints,
} from "@/masks/custom-path";
import type { CustomMask } from "@/masks/types";
import { isMaskableElement, updateElementInSceneTracks } from "@/timeline";
import type { MaskableElement, SceneTracks } from "@/timeline";

function deletePointsFromCustomMask({
	mask,
	pointIds,
}: {
	mask: CustomMask;
	pointIds: string[];
}): CustomMask {
	const points = mask.params.path;
	const nextPoints = removeCustomMaskPoints({ points, pointIds });
	if (nextPoints.length === points.length) {
		return mask;
	}

	return {
		...mask,
		params: {
			...mask.params,
			path: nextPoints,
			closed: getCustomMaskClosedStateAfterPointRemoval({
				wasClosed: mask.params.closed,
				remainingPointCount: nextPoints.length,
			}),
		},
	};
}

function deletePointsFromElementMask({
	element,
	maskId,
	pointIds,
}: {
	element: MaskableElement;
	maskId: string;
	pointIds: string[];
}): { element: MaskableElement; didDeletePoints: boolean } {
	const currentMasks = element.masks ?? [];
	let didDeletePoints = false;
	const nextMasks = currentMasks.map((mask) => {
		if (mask.id !== maskId || mask.type !== "custom") {
			return mask;
		}

		const nextMask = deletePointsFromCustomMask({
			mask,
			pointIds,
		});
		didDeletePoints ||= nextMask !== mask;
		return nextMask;
	});

	return {
		element: didDeletePoints ? { ...element, masks: nextMasks } : element,
		didDeletePoints,
	};
}

export class DeleteCustomMaskPointsCommand extends Command {
	private savedState: SceneTracks | null = null;
	private readonly trackId: string;
	private readonly elementId: string;
	private readonly maskId: string;
	private readonly pointIds: string[];

	constructor({
		trackId,
		elementId,
		maskId,
		pointIds,
	}: {
		trackId: string;
		elementId: string;
		maskId: string;
		pointIds: string[];
	}) {
		super();
		this.trackId = trackId;
		this.elementId = elementId;
		this.maskId = maskId;
		this.pointIds = pointIds;
	}

	execute(): CommandResult | undefined {
		const editor = EditorCore.getInstance();
		this.savedState = editor.scenes.getActiveScene().tracks;

		let didDeletePoints = false;
		const updatedTracks = updateElementInSceneTracks({
			tracks: this.savedState,
			trackId: this.trackId,
			elementId: this.elementId,
			elementPredicate: isMaskableElement,
			update: (element) => {
				const result = deletePointsFromElementMask({
					element: element as MaskableElement,
					maskId: this.maskId,
					pointIds: this.pointIds,
				});
				didDeletePoints ||= result.didDeletePoints;
				return result.element;
			},
		});

		if (didDeletePoints) {
			editor.timeline.updateTracks(updatedTracks);
			return {
				selection: {
					selectedMaskPoints: null,
				},
			};
		}

		return undefined;
	}

	undo(): void {
		if (this.savedState) {
			const editor = EditorCore.getInstance();
			editor.timeline.updateTracks(this.savedState);
		}
	}
}
