(() => {
    'use strict';

    window.FotogenicAI = window.FotogenicAI || {};
    const Studio = window.FotogenicAI.Studio = window.FotogenicAI.Studio || {};
    const { __, _n, sprintf } = Studio.i18n;
    const vm = Studio.viewModel;
    const icons = window.FotogenicAI.Shared?.ui?.icon;
    if (!icons) throw new Error('Fotogenic AI shared icon helper is required.');

    const STATUSES = new Set([
        'idle', 'loading', 'queued', 'starting', 'processing', 'finalizing', 'saving', 'pausing', 'paused',
        'stopping', 'stopped', 'uncertain', 'partial', 'failed', 'completed',
    ]);
    const ERROR_KINDS = new Set([
        'validation', 'insufficient_credits', 'rejected_start', 'uncertain', 'partial_result',
        'finalization_delay', 'failed_start',
    ]);
    const BUSY_STATUSES = new Set([
        'loading', 'queued', 'starting', 'processing', 'finalizing', 'saving', 'pausing', 'stopping',
    ]);
    const TERMINAL_STATUSES = new Set([
        'stopped', 'partial', 'failed', 'completed',
    ]);
	const DELIVERY_MODES = new Set(['media_library', 'review', 'gallery_append', 'featured_review', 'mixed']);
	const normalizeDeliveryMode = (value) => {
		const mode = vm.cleanText(value, 32).toLowerCase();
		return DELIVERY_MODES.has(mode) ? mode : 'media_library';
	};
    const RENDER_KEY_ATTRIBUTE = 'data-fa-render-key';
    const PRESENTATION = Object.freeze({
        idle: [
            __( 'Generation has not started', 'fotogenic-ai' ),
            __( 'There is no accepted generation to monitor yet.', 'fotogenic-ai' ),
        ],
        loading: [
            __( 'Loading generation status', 'fotogenic-ai' ),
            __( 'The accepted request and its current saved state are being loaded.', 'fotogenic-ai' ),
        ],
        queued: [
            __( 'Run queued', 'fotogenic-ai' ),
            __( 'The accepted run is waiting for an available worker.', 'fotogenic-ai' ),
        ],
        starting: [
            __( 'Starting your run', 'fotogenic-ai' ),
            __( 'The accepted request is being prepared. It will not be started twice.', 'fotogenic-ai' ),
        ],
        processing: [
			__( 'Generating your images', 'fotogenic-ai' ),
			__( 'Completed files follow the selected delivery option.', 'fotogenic-ai' ),
        ],
        finalizing: [
            __( 'Saving completed outputs', 'fotogenic-ai' ),
            __( 'Completed outputs are being saved in Media Library.', 'fotogenic-ai' ),
        ],
        saving: [
            __( 'Saving completed outputs', 'fotogenic-ai' ),
            __( 'Completed outputs are being saved in Media Library.', 'fotogenic-ai' ),
        ],
        pausing: [
            __( 'Pausing safely', 'fotogenic-ai' ),
            __( 'Finishing the current safe checkpoint before pausing.', 'fotogenic-ai' ),
        ],
        paused: [
            __( 'Bulk generation paused', 'fotogenic-ai' ),
            __( 'In-flight items are reconciled before a later resume. Saved attachments are kept.', 'fotogenic-ai' ),
        ],
        stopping: [
            __( 'Stopping safely', 'fotogenic-ai' ),
            __( 'In-flight items are finishing before pending work is skipped.', 'fotogenic-ai' ),
        ],
        stopped: [
            __( 'Bulk generation stopped', 'fotogenic-ai' ),
            __( 'Pending items were skipped after in-flight work finished. Saved attachments are kept.', 'fotogenic-ai' ),
        ],
        uncertain: [
            __( 'Run status needs reconciliation', 'fotogenic-ai' ),
            __( 'Check the existing request identity before taking another action. No new billable start will be sent.', 'fotogenic-ai' ),
        ],
        partial: [
            __( 'Run completed with exceptions', 'fotogenic-ai' ),
            __( 'Saved outputs remain available. Failed items can be retried against this accepted run.', 'fotogenic-ai' ),
        ],
        failed: [
            __( 'Run needs attention', 'fotogenic-ai' ),
            __( 'The accepted run stopped before every item completed. Review recovery options below.', 'fotogenic-ai' ),
        ],
        completed: [
            __( 'Generation complete', 'fotogenic-ai' ),
            __( 'Every completed output is saved in Media Library.', 'fotogenic-ai' ),
        ],
    });
    const ERROR_COPY = Object.freeze({
        validation: [__( 'Review the generation details', 'fotogenic-ai' ), __( 'One or more inputs need attention before generation can start.', 'fotogenic-ai' )],
        insufficient_credits: [__( 'More credits are required', 'fotogenic-ai' ), __( 'Your current balance is not enough for this generation.', 'fotogenic-ai' )],
        rejected_start: [__( 'Generation was not started', 'fotogenic-ai' ), __( 'The request was safely rejected before a billable generation was admitted.', 'fotogenic-ai' )],
        uncertain: [__( 'Generation status is uncertain', 'fotogenic-ai' ), __( 'Check the same request status before taking another action. Do not start it again.', 'fotogenic-ai' )],
        partial_result: [__( 'Some results need attention', 'fotogenic-ai' ), __( 'Available images are preserved. Review completed and unavailable results separately.', 'fotogenic-ai' )],
        finalization_delay: [__( 'Results are still being saved', 'fotogenic-ai' ), __( 'Generation may be complete, but Media Library finalization is not confirmed.', 'fotogenic-ai' )],
        failed_start: [__( 'Generation could not be started', 'fotogenic-ai' ), __( 'Review the safe error details and return to Configure before trying a new request.', 'fotogenic-ai' )],
    });

	const progressDeliveryCopy = (mode = 'media_library') => ({
		media_library: {
			processing: __( 'Completed files are saved in Media Library as they become ready.', 'fotogenic-ai' ),
			saving: __( 'Completed files are being saved in Media Library.', 'fotogenic-ai' ),
			complete: __( 'Completed files are ready in Media Library.', 'fotogenic-ai' ),
			savingStep: __( 'Saving completed files in Media Library.', 'fotogenic-ai' ),
			summary: __( 'Review the saved files and continue with any available actions.', 'fotogenic-ai' ),
		},
		review: {
			processing: __( 'Completed files are saved for review before any product image changes.', 'fotogenic-ai' ),
			saving: __( 'Completed files are being saved for product review.', 'fotogenic-ai' ),
			complete: __( 'Completed files are ready for product review.', 'fotogenic-ai' ),
			savingStep: __( 'Saving completed files for product review.', 'fotogenic-ai' ),
			summary: __( 'Review saved results before applying product image changes.', 'fotogenic-ai' ),
		},
		gallery_append: {
			processing: __( 'Completed files are saved and added to eligible product galleries.', 'fotogenic-ai' ),
			saving: __( 'Completed files are being saved and added to eligible product galleries.', 'fotogenic-ai' ),
			complete: __( 'Completed files were saved and eligible product galleries were updated.', 'fotogenic-ai' ),
			savingStep: __( 'Saving files and updating eligible product galleries.', 'fotogenic-ai' ),
			summary: __( 'Review saved results and the gallery updates.', 'fotogenic-ai' ),
		},
		featured_review: {
			processing: __( 'Completed files are saved for review before featured image changes.', 'fotogenic-ai' ),
			saving: __( 'Completed files are being saved for featured image review.', 'fotogenic-ai' ),
			complete: __( 'Completed files are ready for featured image review.', 'fotogenic-ai' ),
			savingStep: __( 'Saving completed files for featured image review.', 'fotogenic-ai' ),
			summary: __( 'Review saved results before applying featured image changes.', 'fotogenic-ai' ),
		},
		mixed: {
			processing: __( 'Completed files follow each item\'s selected delivery option.', 'fotogenic-ai' ),
			saving: __( 'Completed files are being saved and delivered by item.', 'fotogenic-ai' ),
			complete: __( 'Completed files were saved and delivered by item.', 'fotogenic-ai' ),
			savingStep: __( 'Saving and delivering completed files by item.', 'fotogenic-ai' ),
			summary: __( 'Review saved results and delivery outcomes.', 'fotogenic-ai' ),
		},
	}[normalizeDeliveryMode(mode)]);

    const presentationFor = (model) => {
        const presentation = PRESENTATION[model.status];
		const delivery = progressDeliveryCopy(model.deliveryMode);
		if (model.status === 'processing') return [
			model.workflow === 'campaign_set' ? __( 'Generating your campaign images', 'fotogenic-ai' ) : __( 'Generating your images', 'fotogenic-ai' ),
			delivery.processing,
		];
		if (['finalizing', 'saving'].includes(model.status)) return [__( 'Saving files', 'fotogenic-ai' ), delivery.saving];
		if (model.status === 'completed') return [__( 'Generation complete', 'fotogenic-ai' ), delivery.complete];
        return presentation;
    };

    const normalizeCounts = (value = {}) => {
        const completed = vm.nonNegativeInteger(value.completed, 0, 100000);
        const failed = vm.nonNegativeInteger(value.failed, 0, 100000);
        const requestedTotal = vm.nonNegativeInteger(value.total, 0, 100000);
        const total = Math.max(requestedTotal, completed + failed);
        const processing = Math.min(
            Math.max(0, total - completed - failed),
            vm.nonNegativeInteger(value.processing, Math.max(0, total - completed - failed), 100000)
        );
        return Object.freeze({ total, completed, failed, processing });
    };

    const normalizeProgress = (value = {}) => {
        const route = vm.normalizeRoute(value);
        const rawStatus = vm.cleanText(value.status, 32).toLowerCase();
        const status = STATUSES.has(rawStatus) ? rawStatus : (rawStatus ? 'uncertain' : 'idle');
        const counts = normalizeCounts(value.counts);
        const activeSource = value.activeSource && typeof value.activeSource === 'object'
            ? Object.freeze({
                label: vm.cleanText(value.activeSource.label, 200),
                meta: vm.cleanText(value.activeSource.meta, 200),
                imageUrl: vm.safeUrl(value.activeSource.imageUrl ?? value.activeSource.url),
            })
            : null;
        return Object.freeze({
            ...route,
            status,
			deliveryMode: normalizeDeliveryMode(value.deliveryMode),
			allResultsRemoved: status === 'completed' && value.allResultsRemoved === true,
            counts,
            message: vm.cleanText(value.message, 500),
            pollIntervalSeconds: Math.max(0, Math.min(60, Number(value.pollIntervalSeconds) || 0)),
            activeSource,
            controls: Object.freeze({
                pause: value.controls?.pause === true,
                resume: value.controls?.resume === true,
                stop: value.controls?.stop === true,
                checkStatus: value.controls?.checkStatus === true,
                viewResults: value.controls?.viewResults === true,
            }),
        });
    };

    const normalizeError = (value = {}) => {
        const rawKind = vm.cleanText(value.kind, 64).toLowerCase();
        const kind = ERROR_KINDS.has(rawKind) ? rawKind : 'failed_start';
        return Object.freeze({
            kind,
            message: vm.cleanText(value.message, 500),
            code: vm.safeCode(value.code),
            canCheckStatus: value.canCheckStatus === true || ['uncertain', 'finalization_delay', 'partial_result'].includes(kind),
            canViewResults: value.canViewResults === true || kind === 'partial_result',
            canReturn: value.canReturn !== false && !['uncertain', 'finalization_delay'].includes(kind),
        });
    };

    const appendSource = (container, source, fallbackLabel) => {
        const media = vm.element('span', 'fotogenic-ai-progress-source__media');
        if (source?.imageUrl) {
            const image = vm.element('img');
            image.src = source.imageUrl;
            image.alt = '';
            image.decoding = 'async';
            media.appendChild(image);
        } else {
            media.appendChild(icons.create('image-square', { size: 24 }));
        }
        const copy = vm.element('span', 'fotogenic-ai-progress-source__copy');
        copy.append(
            vm.element('strong', '', source?.label || fallbackLabel),
            vm.element('small', '', source?.meta || __( 'Exact accepted source', 'fotogenic-ai' ))
        );
        container.append(media, copy);
    };

    const statusIconName = (status) => {
		if (status === 'completed') return 'check-circle';
		if (status === 'removed') return 'trash';
        if (status === 'paused') return 'pause';
		if (['failed', 'partial', 'uncertain'].includes(status)) return 'warning';
        if (status === 'stopped') return 'stop-circle';
		if (['starting', 'processing', 'finalizing', 'saving'].includes(status)) return 'arrows-clockwise';
        return 'clock-counter-clockwise';
    };

    const statusIconWeight = (status) => (
		['completed', 'removed', 'paused', 'failed', 'partial', 'uncertain', 'stopped'].includes(status)
            ? 'fill'
            : 'regular'
    );

    const statusLabel = (status) => ({
        idle: __( 'Not started', 'fotogenic-ai' ),
        loading: __( 'Loading', 'fotogenic-ai' ),
        queued: __( 'Queued', 'fotogenic-ai' ),
        starting: __( 'Preparing', 'fotogenic-ai' ),
        processing: __( 'Generating', 'fotogenic-ai' ),
        finalizing: __( 'Finalizing outputs', 'fotogenic-ai' ),
        saving: __( 'Saving files', 'fotogenic-ai' ),
        pausing: __( 'Pausing safely', 'fotogenic-ai' ),
        paused: __( 'Paused · ready to resume', 'fotogenic-ai' ),
        stopping: __( 'Stopping safely', 'fotogenic-ai' ),
        stopped: __( 'Stopped', 'fotogenic-ai' ),
        uncertain: __( 'Status needs reconciliation', 'fotogenic-ai' ),
        partial: __( 'Completed with exceptions', 'fotogenic-ai' ),
        failed: __( 'Failed', 'fotogenic-ai' ),
        skipped: __( 'Skipped before starting', 'fotogenic-ai' ),
        completed: __( 'Saved', 'fotogenic-ai' ),
		removed: __( 'Removed', 'fotogenic-ai' ),
    })[status] || __( 'Status unavailable', 'fotogenic-ai' );

    const setRenderKey = (node, key) => {
        if (node && key) node.setAttribute(RENDER_KEY_ATTRIBUTE, String(key));
        return node;
    };

    const syncAttributes = (current, next) => {
        const nextNames = new Set(next.getAttributeNames());
        current.getAttributeNames().forEach((name) => {
            if (!nextNames.has(name)) current.removeAttribute(name);
        });
        nextNames.forEach((name) => current.setAttribute(name, next.getAttribute(name)));
        current.className = next.className;
        current.id = next.id;
        current.hidden = next.hidden;
        current.inert = next.inert;
        current.tabIndex = next.tabIndex;
    };

    const reconcileElement = (currentRoot, nextRoot) => {
        if (!currentRoot || !nextRoot || currentRoot.tagName !== nextRoot.tagName) return nextRoot;
        const currentByKey = new Map(
            [...currentRoot.querySelectorAll(`[${RENDER_KEY_ATTRIBUTE}]`)]
                .map((node) => [node.getAttribute(RENDER_KEY_ATTRIBUTE), node])
                .filter(([key]) => Boolean(key))
        );
        [...nextRoot.querySelectorAll(`[${RENDER_KEY_ATTRIBUTE}]`)].reverse().forEach((nextNode) => {
            const currentNode = currentByKey.get(nextNode.getAttribute(RENDER_KEY_ATTRIBUTE));
            if (!currentNode || currentNode.tagName !== nextNode.tagName) return;
            syncAttributes(currentNode, nextNode);
            if (nextNode.getAttribute('data-fa-preserve-children') === null) {
                const nextChildren = [...nextNode.childNodes];
                if (nextChildren.length) currentNode.replaceChildren(...nextChildren);
                else currentNode.textContent = nextNode.textContent;
            }
            nextNode.replaceWith(currentNode);
        });
        syncAttributes(currentRoot, nextRoot);
        currentRoot.replaceChildren(...nextRoot.childNodes);
        return currentRoot;
    };

    const createProgressBar = ({ completed = 0, total = 0, active = false, renderKey = '' } = {}) => {
        const indeterminate = active && completed === 0;
        const mode = indeterminate ? 'indeterminate' : 'determinate';
        const track = setRenderKey(
            vm.element('span', `fotogenic-ai-progress-bar${indeterminate ? ' is-indeterminate' : ''}`),
            renderKey ? `${renderKey}:${mode}` : ''
        );
        track.setAttribute('aria-hidden', 'true');
        const value = setRenderKey(vm.element('i'), indeterminate && renderKey ? `${renderKey}:${mode}:value` : '');
        const percent = total > 0 ? Math.min(100, Math.max(0, (completed / total) * 100)) : 0;
        if (!indeterminate) value.style.inlineSize = `${percent}%`;
        track.appendChild(value);
        return track;
    };

    const timelineState = (status, step) => {
        const generationComplete = ['finalizing', 'saving', ...TERMINAL_STATUSES].includes(status);
        const savingComplete = TERMINAL_STATUSES.has(status);
        if (step === 0) return 'is-complete';
        if (step === 1) return generationComplete ? 'is-complete' : 'is-current';
        if (step === 2) return savingComplete ? 'is-complete' : (generationComplete ? 'is-current' : '');
        return TERMINAL_STATUSES.has(status) ? 'is-complete' : '';
    };

    const createTimeline = (status, allResultsRemoved = false, renderKey = 'live-run', deliveryMode = 'media_library') => {
        const timeline = vm.element('ol', 'fotogenic-ai-progress-timeline');
		const delivery = progressDeliveryCopy(deliveryMode);
        const steps = [
            [__( 'Start accepted', 'fotogenic-ai' ), __( 'Your billable start has been accepted.', 'fotogenic-ai' )],
            [__( 'Generating', 'fotogenic-ai' ), __( 'Creating outputs from source items.', 'fotogenic-ai' )],
			[TERMINAL_STATUSES.has(status) ? __( 'Files saved', 'fotogenic-ai' ) : __( 'Saving files', 'fotogenic-ai' ),
				TERMINAL_STATUSES.has(status) ? delivery.complete : delivery.savingStep],
			[
				TERMINAL_STATUSES.has(status) ? __( 'Summary ready', 'fotogenic-ai' ) : __( 'Summary', 'fotogenic-ai' ),
				allResultsRemoved
					? __( 'The source is unchanged. Reopen setup for a new generation or return to Run history.', 'fotogenic-ai' )
					: delivery.summary,
			],
        ];
        steps.forEach(([title, detail], index) => {
            const state = timelineState(status, index);
            const item = vm.element('li', state);
            if (state === 'is-complete') {
                item.appendChild(icons.create('check-circle', { weight: 'fill', size: 19 }));
            } else if (state === 'is-current' && status === 'paused') {
                item.appendChild(icons.create('pause', { weight: 'fill', size: 19 }));
            } else if (state === 'is-current' && BUSY_STATUSES.has(status)) {
                const icon = setRenderKey(
                    icons.create('arrows-clockwise', { size: 19 }),
                    `${renderKey}:timeline:${index}:${status}`
                );
				icon.classList.add('fotogenic-ai-icon--spin');
                item.appendChild(icon);
            } else {
                item.appendChild(vm.element('span', 'fotogenic-ai-progress-timeline__dot'));
            }
            if (state === 'is-current') item.setAttribute('aria-current', 'step');
            const copy = vm.element('span');
            copy.append(vm.element('strong', '', title), vm.element('small', '', detail));
            item.appendChild(copy);
            timeline.appendChild(item);
        });
        return timeline;
    };

    const createLiveRunCard = ({ status, items, outputs, saved, failed, allResultsRemoved = false, loaded = false, pollIntervalSeconds = 0, renderKey = 'live-run', deliveryMode = 'media_library' } = {}) => {
        const card = setRenderKey(vm.element('aside', 'fotogenic-ai-progress-live-run'), renderKey);
        const heading = vm.element('h2', '', __( 'Live run', 'fotogenic-ai' ));
        heading.id = 'fotogenic-ai-live-run-title';
        card.setAttribute('aria-labelledby', heading.id);
        const metrics = vm.element('dl', 'fotogenic-ai-progress-metrics');
        [
            [loaded ? __( 'Loaded items', 'fotogenic-ai' ) : __( 'Items', 'fotogenic-ai' ), items],
            [loaded ? __( 'Loaded outputs', 'fotogenic-ai' ) : __( 'Outputs', 'fotogenic-ai' ), outputs],
            [__( 'Saved', 'fotogenic-ai' ), saved],
            [__( 'Failed', 'fotogenic-ai' ), failed],
        ].forEach(([label, value]) => {
            const metric = vm.element('div');
            metric.append(vm.element('dt', '', label), vm.element('dd', '', String(value)));
            metrics.appendChild(metric);
        });
        const footer = vm.element('footer', 'fotogenic-ai-progress-connection');
        const dot = vm.element('i');
        dot.setAttribute('aria-hidden', 'true');
        const pollingCopy = __( 'Connected', 'fotogenic-ai' );
        const connection = status === 'uncertain'
            ? __( 'Status unconfirmed · accepted identity retained', 'fotogenic-ai' )
            : (BUSY_STATUSES.has(status) || status === 'paused')
                ? pollingCopy
				: (status === 'completed'
					? (allResultsRemoved
						? __( 'Completed · generated attachments deleted', 'fotogenic-ai' )
						: __( 'Completed · saved attachments retained', 'fotogenic-ai' ))
					: sprintf( __( '%s · saved attachments retained', 'fotogenic-ai' ), statusLabel(status) ));
        footer.append(dot, vm.element('span', '', connection));
		card.append(heading, metrics, createTimeline(status, allResultsRemoved, renderKey, deliveryMode), footer);
        return card;
    };

    const createSingleWorkspace = (model) => {
        const grid = setRenderKey(vm.element('div', 'fotogenic-ai-progress-grid'), 'single:grid');
        const ledger = setRenderKey(vm.element('section', 'fotogenic-ai-progress-ledger'), 'single:ledger');
        const header = vm.element('header', 'fotogenic-ai-progress-command-header');
        const heading = vm.element('h2', '', __( 'Run items', 'fotogenic-ai' ));
        heading.id = 'fotogenic-ai-run-items-title';
        ledger.setAttribute('aria-labelledby', heading.id);
        const itemCount = ['idle', 'loading'].includes(model.status) ? 0 : 1;
        const itemCountCopy = model.sourceType === 'product'
            ? sprintf(_n( '%d product', '%d products', itemCount, 'fotogenic-ai' ), itemCount)
            : sprintf(_n( '%d item', '%d items', itemCount, 'fotogenic-ai' ), itemCount);
        header.append(heading, vm.element('span', 'fotogenic-ai-progress-run-count', itemCountCopy));
        const columns = vm.element('div', 'fotogenic-ai-progress-columns');
        [__( 'Source', 'fotogenic-ai' ), __( 'Outputs', 'fotogenic-ai' ), __( 'Status', 'fotogenic-ai' )]
            .forEach((label) => columns.appendChild(vm.element('span', '', label)));
        columns.setAttribute('aria-hidden', 'true');
        const rows = setRenderKey(vm.element('div', 'fotogenic-ai-progress-rows'), 'single:rows');
        rows.id = 'fotogenic-ai-progress-rows';
        if (model.status === 'idle' || model.status === 'loading') {
            const empty = vm.element('p', 'fotogenic-ai-progress-empty', model.status === 'loading'
                ? __( 'Loading current run items…', 'fotogenic-ai' )
                : __( 'No accepted run items are available yet.', 'fotogenic-ai' ));
            if (model.status === 'loading') empty.setAttribute('role', 'status');
            rows.appendChild(empty);
        } else {
            const row = setRenderKey(vm.element('article', `fotogenic-ai-progress-row is-${model.status}`), 'single:row');
            if (BUSY_STATUSES.has(model.status)) row.classList.add('is-current');
            if (BUSY_STATUSES.has(model.status)) row.setAttribute('aria-current', 'step');
            const source = vm.element('div', 'fotogenic-ai-progress-source');
			const sourceIdentity = JSON.stringify([
				model.sourceType,
				model.activeSource?.label || '',
				model.activeSource?.imageUrl || '',
			]);
			source.setAttribute('data-fa-render-key', `single:source:${sourceIdentity}`);
            source.setAttribute('data-fa-preserve-children', '');
            appendSource(source, model.activeSource, __( 'Current source', 'fotogenic-ai' ));
            const output = vm.element('div', 'fotogenic-ai-progress-output');
            output.setAttribute('data-fa-render-key', 'single:output');
            const completed = Math.min(model.counts.total, model.counts.completed);
            output.append(
                vm.element('span', '', model.counts.total
                    ? sprintf( __( '%1$d of %2$d', 'fotogenic-ai' ), completed, model.counts.total )
					: __( 'Not available', 'fotogenic-ai' )),
                createProgressBar({
                    completed,
                    total: model.counts.total,
                    active: BUSY_STATUSES.has(model.status),
                    renderKey: 'single:progress',
                })
            );
            const status = setRenderKey(
                vm.element('div', 'fotogenic-ai-progress-item-status'),
                'single:status'
            );
            const statusIcon = icons.create(statusIconName(model.status), { weight: statusIconWeight(model.status), size: 18 });
			statusIcon.setAttribute(RENDER_KEY_ATTRIBUTE, `single:status-icon:${model.status}`);
			if (BUSY_STATUSES.has(model.status)) statusIcon.classList.add('fotogenic-ai-icon--spin');
            status.append(statusIcon, vm.element('span', '', statusLabel(model.status)));
            row.append(source, output, status);
            rows.appendChild(row);
        }
        ledger.append(header, columns, rows);
        grid.append(
            ledger,
            createLiveRunCard({
                status: model.status,
                items: ['idle', 'loading'].includes(model.status) ? 0 : 1,
                outputs: model.counts.total,
                saved: model.counts.completed,
                failed: model.counts.failed,
				allResultsRemoved: model.allResultsRemoved,
				pollIntervalSeconds: model.pollIntervalSeconds,
				deliveryMode: model.deliveryMode,
                renderKey: 'single:live-run',
            })
        );
        return grid;
    };

    const createProgressView = ({ mount, errorMount, announce = () => {} } = {}) => {
        let currentModel = null;
        let currentHandlers = {};
        let currentError = null;
        let currentErrorHandlers = {};
        let pending = '';
        let actionError = '';
        let lastViewKey = '';
        let lastAnnouncement = '';
        let lastPresentationSignature = '';
        let workspace = null;
        let bulkMount = null;
        let destroyed = false;

        const presentationSignature = (model) => {
			const { pollIntervalSeconds: _pollIntervalSeconds, ...presentationModel } = model;
			return JSON.stringify({
            model: presentationModel,
            pending,
            actionError,
            handlerCapabilities: Object.fromEntries([
                'checkStatus', 'pause', 'resume', 'stop', 'openSummary',
            ].map((name) => [name, typeof currentHandlers[name] === 'function'])),
        });
		};

        const invoke = async (name, callback, payload, navigation = false) => {
            if (destroyed || pending || typeof callback !== 'function') return;
            const origin = currentError ? errorMount : mount;
            const restoreKey = origin?.contains?.(document.activeElement)
                ? document.activeElement.getAttribute?.('data-fa-focus-key') || ''
                : '';
            pending = name;
            actionError = '';
            currentError
                ? renderError(currentError, currentErrorHandlers, { preserveActionError: true, announce: false })
                : render(currentModel, currentHandlers, { preserveActionError: true });
            let navigated = false;
            try {
                await callback(payload);
                navigated = navigation;
            } catch (error) {
                Studio.errors?.normalizeAndReport?.(error, { source: 'ui' });
                actionError = __( 'The requested action could not be completed. Check the current status before trying again.', 'fotogenic-ai' );
                announce(actionError);
            } finally {
                pending = '';
                if (!destroyed && !navigated) {
                    currentError
                        ? renderError(currentError, currentErrorHandlers, { preserveActionError: true, announce: Boolean(actionError) })
                        : render(currentModel, currentHandlers, { preserveActionError: true });
                    if (restoreKey) {
                        [...origin.querySelectorAll('[data-fa-focus-key]')]
                            .find((node) => node.getAttribute?.('data-fa-focus-key') === restoreKey && !node.disabled)
                            ?.focus?.({ preventScroll: true });
                    }
                }
            }
        };

        const actionButton = (label, action, callback, payload, navigation = false) => {
            const control = vm.button(pending === action ? `${label}…` : label, 'secondary');
            control.setAttribute('data-fa-focus-key', `progress:${action}`);
            control.disabled = Boolean(pending);
            control.setAttribute('aria-busy', pending === action ? 'true' : 'false');
            control.addEventListener('click', () => void invoke(action, callback, payload, navigation));
            return control;
        };

        const announceProgress = (model) => {
            const milestone = model.counts.total > 0
                ? Math.min(4, Math.floor(((model.counts.completed + model.counts.failed) / model.counts.total) * 4))
                : 0;
            const key = `${model.status}:${milestone}`;
            if (key === lastAnnouncement) return;
            lastAnnouncement = key;
            const presentation = presentationFor(model);
            announce(sprintf(
                __( '%1$s. %2$d complete, %3$d failed.', 'fotogenic-ai' ),
                presentation[0],
                model.counts.completed,
                model.counts.failed
            ));
        };

        const render = (value, handlers = {}, options = {}) => {
            if (destroyed || !mount) return null;
            const active = document.activeElement;
            const restoreFocusKey = mount.contains?.(active)
                ? String(active.getAttribute?.('data-fa-focus-key') || '')
                : '';
            if (options.preserveActionError !== true) actionError = '';
            currentModel = normalizeProgress(value);
            currentHandlers = handlers && typeof handlers === 'object' ? handlers : {};
            currentError = null;
            const model = currentModel;
            const nextPresentationSignature = presentationSignature(model);
            if (nextPresentationSignature === lastPresentationSignature && workspace?.parentElement) {
                if (options.focus === true) vm.focusHeading(mount);
                announceProgress(model);
                return Object.freeze({ model, bulkMount });
            }
            const viewKey = `progress:${model.workflow}:${model.scope}`;
            const canReconcile = workspace?.parentElement && lastViewKey === viewKey;
            if (!canReconcile) bulkMount = null;
            const presentation = presentationFor(model);
            const nextWorkspace = vm.element('div', `fotogenic-ai-progress-workspace is-${model.status}`);
            nextWorkspace.setAttribute('aria-busy', BUSY_STATUSES.has(model.status) ? 'true' : 'false');
            const intro = vm.element('header', 'fotogenic-ai-progress-intro');
            const copy = vm.element('div');
            copy.appendChild(vm.element('span', 'fotogenic-ai-progress-eyebrow', __( 'Step 3 of 4 · Generate', 'fotogenic-ai' )));
            const heading = vm.element('h2', '', presentation[0]);
            heading.tabIndex = -1;
			copy.append(heading, vm.element('p', '', model.message || (model.allResultsRemoved
				? __( 'Generated attachments were deleted from Media Library. The source is unchanged.', 'fotogenic-ai' )
				: presentation[1])));
            const controls = setRenderKey(vm.element('div', 'fotogenic-ai-progress-controls'), 'progress:controls');
            controls.setAttribute('data-fa-progress-controls', '');
            controls.setAttribute('aria-label', __( 'Run controls', 'fotogenic-ai' ));
            if ((model.status === 'uncertain' || model.controls.checkStatus) && typeof currentHandlers.checkStatus === 'function') {
                const check = actionButton(__( 'Check status', 'fotogenic-ai' ), 'checkStatus', currentHandlers.checkStatus, model);
                check.className = 'primary';
                check.prepend(icons.create('arrows-clockwise', { size: 16 }));
                controls.appendChild(check);
            }
            if (
                TERMINAL_STATUSES.has(model.status)
                && model.controls.viewResults
                && typeof currentHandlers.openSummary === 'function'
            ) {
                const summary = actionButton(__( 'Open summary', 'fotogenic-ai' ), 'openSummary', currentHandlers.openSummary, model, true);
                summary.className = 'primary';
                summary.appendChild(icons.create('arrow-right', { size: 16 }));
                controls.appendChild(summary);
            }
            intro.append(copy, controls);
            nextWorkspace.appendChild(intro);

            if (model.scope === 'bulk') {
                const nextBulkMount = setRenderKey(vm.element('section', 'fotogenic-ai-progress-bulk'), 'progress:bulk-mount');
                nextBulkMount.setAttribute('data-fa-preserve-children', '');
                nextBulkMount.setAttribute('aria-label', __( 'Bulk generation monitor', 'fotogenic-ai' ));
                if (restoreFocusKey.startsWith('bulk:')) nextBulkMount.setAttribute('data-fa-restore-focus-key', restoreFocusKey);
                nextWorkspace.appendChild(nextBulkMount);
            } else {
                nextWorkspace.appendChild(createSingleWorkspace(model));
            }
            if (actionError) nextWorkspace.appendChild(vm.element('p', 'fotogenic-ai-progress-action-error', actionError));
            const live = vm.element('p', 'fotogenic-ai-studio-sr-only');
            live.setAttribute('aria-live', 'polite');
            live.textContent = sprintf(
                __( 'Current run stage: %1$s. %2$d of %3$d outputs completed.', 'fotogenic-ai' ),
                presentation[0],
                model.counts.completed,
                model.counts.total
            );
            nextWorkspace.appendChild(live);
            if (canReconcile) {
                const surfaceClasses = workspace.className.split(/\s+/).filter((name) => name && !name.startsWith('is-'));
                reconcileElement(workspace, nextWorkspace);
                workspace.className = [...surfaceClasses, `is-${model.status}`].join(' ');
            } else {
                workspace = nextWorkspace;
                mount.replaceChildren(workspace);
            }
            bulkMount = model.scope === 'bulk'
                ? workspace.querySelector(`[${RENDER_KEY_ATTRIBUTE}="progress:bulk-mount"]`)
                : null;
            lastPresentationSignature = nextPresentationSignature;

            if (restoreFocusKey.startsWith('progress:')) {
                [...mount.querySelectorAll('[data-fa-focus-key]')]
                    .find((node) => node.getAttribute?.('data-fa-focus-key') === restoreFocusKey && !node.disabled)
                    ?.focus?.({ preventScroll: true });
            }
            if (options.focus === true || lastViewKey !== viewKey) vm.focusHeading(mount);
            lastViewKey = viewKey;
            announceProgress(model);
            return Object.freeze({ model, bulkMount });
        };

        const renderError = (value, handlers = {}, options = {}) => {
            if (destroyed || !errorMount) return null;
            if (options.preserveActionError !== true) actionError = '';
            currentError = normalizeError(value);
            lastPresentationSignature = '';
            currentErrorHandlers = handlers && typeof handlers === 'object' ? handlers : {};
            const model = currentError;
            const copy = ERROR_COPY[model.kind];
            const workspace = vm.element('div', 'fotogenic-ai-progress-workspace fotogenic-ai-progress-error');
            const intro = vm.element('header', 'fotogenic-ai-progress-intro');
            const introCopy = vm.element('div');
            introCopy.appendChild(vm.element('span', 'fotogenic-ai-progress-eyebrow', __( 'Step 3 of 4 · Generate', 'fotogenic-ai' )));
            const heading = vm.element('h2', '', copy[0]);
            heading.tabIndex = -1;
            introCopy.append(heading, vm.element('p', '', model.message || copy[1]));
            intro.appendChild(introCopy);
            workspace.appendChild(intro);
            const recovery = vm.element('section', 'fotogenic-ai-progress-recovery');
            recovery.append(icons.create('warning', { weight: 'fill', size: 18 }), vm.element('div', '', copy[1]));
            if (model.code) recovery.appendChild(vm.element('p', '', sprintf( __( 'Reference code: %s', 'fotogenic-ai' ), model.code )));
            if (actionError) recovery.appendChild(vm.element('p', 'fotogenic-ai-progress-action-error', actionError));
            const actions = vm.element('div', 'fotogenic-ai-progress-recovery__actions');
            if (model.canViewResults && typeof currentErrorHandlers.viewResults === 'function') {
                actions.appendChild(actionButton(__( 'View available results', 'fotogenic-ai' ), 'viewResults', currentErrorHandlers.viewResults, model, true));
            }
            if (model.canCheckStatus && typeof currentErrorHandlers.checkStatus === 'function') {
                const check = actionButton(__( 'Check status', 'fotogenic-ai' ), 'checkStatus', currentErrorHandlers.checkStatus, model);
                check.className = 'primary';
                actions.appendChild(check);
            }
            if (model.canReturn && typeof currentErrorHandlers.backToCompose === 'function') {
                actions.appendChild(actionButton(__( 'Back to Configure', 'fotogenic-ai' ), 'backToCompose', currentErrorHandlers.backToCompose, model, true));
            }
            recovery.appendChild(actions);
            workspace.appendChild(recovery);
            errorMount.replaceChildren(workspace);
            const viewKey = `error:${model.kind}`;
            if (options.focus === true || lastViewKey !== viewKey) vm.focusHeading(errorMount);
            lastViewKey = viewKey;
            if (options.announce !== false) announce(actionError || `${copy[0]}. ${model.message || copy[1]}`);
            return model;
        };

        return Object.freeze({
            render,
            renderError,
            getBulkMount: () => bulkMount,
            destroy() {
                destroyed = true;
                mount?.replaceChildren?.();
                errorMount?.replaceChildren?.();
                currentModel = null;
                currentHandlers = {};
                currentError = null;
                currentErrorHandlers = {};
                lastPresentationSignature = '';
                workspace = null;
                bulkMount = null;
            },
        });
    };

    Studio.progress = Object.freeze({
        createProgressView,
        createLiveRunCard,
        createProgressBar,
        reconcileElement,
        normalizeProgress,
        normalizeError,
        normalizeCounts,
        statusIconName,
        statusIconWeight,
        statusLabel,
        timelineState,
    });
})();
