(() => {
    'use strict';

    window.FotogenicAI = window.FotogenicAI || {};
    const Studio = window.FotogenicAI.Studio = window.FotogenicAI.Studio || {};
    const { __, _n, sprintf, labels } = 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 historyStyles = Studio.historyStyles;
    if (!historyStyles || typeof historyStyles !== 'object') throw new Error('Fotogenic AI History styles are required.');
    const style = (...names) => names.filter(Boolean).map((name) => {
        const value = historyStyles[name];
        if (!value) throw new Error(`Fotogenic AI History style is missing: ${name}`);
        return value;
    }).join(' ');
    const historyButton = (label = '', className = '') => {
        const control = vm.button(label, className);
        control.className = className;
        return control;
    };
    const RUN_STATUSES = new Set(['queued', 'starting', 'processing', 'paused', 'stopping', 'uncertain', 'partial', 'completed', 'failed', 'stopped', 'removed']);
    const HISTORY_PAGE_SIZES = new Set([5, 10, 25]);
    const SOURCE_LABELS = Object.freeze({
        product: __( 'Product', 'fotogenic-ai' ),
        media: __( 'Media', 'fotogenic-ai' ),
        post: __( 'Post', 'fotogenic-ai' ),
        page: __( 'Page', 'fotogenic-ai' ),
    });
    const normalizeRun = (value = {}) => {
        const route = vm.normalizeRoute(value);
        const runId = vm.cleanText(value.runId, 64);
        if (!runId) return null;
        const rawStatus = vm.cleanText(value.status, 32).toLowerCase();
        const status = RUN_STATUSES.has(rawStatus) ? rawStatus : 'uncertain';
        const rawAttachments = Array.isArray(value.attachments) ? value.attachments.slice(0, 12) : [];
        const deletable = new Set(rawAttachments
            .filter((attachment) => attachment?.canDelete === true)
            .map((attachment) => vm.positiveInteger(attachment?.attachmentId))
            .filter(Boolean));
        const resumeIdentity = value.resumeIdentity && typeof value.resumeIdentity === 'object'
            ? value.resumeIdentity
            : null;
        const targetLinks = value.targetLinks && typeof value.targetLinks === 'object' && !Array.isArray(value.targetLinks)
            ? Object.freeze({
                editUrl: vm.safeUrl(value.targetLinks.editUrl),
                viewUrl: vm.safeUrl(value.targetLinks.viewUrl),
            })
            : null;
		const aggregateQueue = ['bulk', 'store_queue'].includes(route.scope);
        const itemCount = aggregateQueue
            ? vm.positiveInteger(resumeIdentity?.selectedCount, 1, 10000)
            : 1;
        return Object.freeze({
            ...route,
            runId,
            requestId: vm.cleanText(value.requestId, 36).toLowerCase(),
            subjectId: vm.positiveInteger(value.subjectId),
            label: vm.cleanText(value.label, 200) || sprintf( __( '%s result', 'fotogenic-ai' ), labels[route.workflow] ),
            status,
            createdLabel: vm.cleanText(value.createdLabel, 100),
            message: vm.cleanText(value.message, 300),
            itemCount,
            totalOutputs: vm.nonNegativeInteger(value.totalOutputs, 0, 10000),
            availableActions: Array.isArray(value.availableActions) ? value.availableActions.slice(0, 10) : [],
			unappliedActions: (Array.isArray(value.unappliedActions) ? value.unappliedActions : [])
				.map((action) => vm.cleanText(action, 64).toLowerCase())
				.filter(Boolean)
				.slice(0, 10),
			appliedAction: vm.cleanText(value.appliedAction, 64).toLowerCase(),
			appliedAttachmentId: vm.positiveInteger(value.appliedAttachmentId),
			deliveryPending: value.deliveryPending === true,
			deliveryFailed: value.deliveryFailed === true,
			deliveryRetryAvailable: value.deliveryRetryAvailable === true,
			deliveryLabel: vm.cleanText(value.deliveryLabel, 120),
            groups: Array.isArray(value.groups) ? value.groups.slice(0, 50) : [],
            source: value.source && typeof value.source === 'object' ? value.source : null,
			sources: vm.normalizeAttachments(value.sources, 10),
			sourceMissing: value.sourceMissing === true,
			currentProduct: vm.normalizeAttachment(value.currentProduct),
			currentProductMissing: value.currentProductMissing === true,
            targetIdentity: value.targetIdentity && typeof value.targetIdentity === 'object'
                ? value.targetIdentity
                : null,
			targetLinks,
			quality: vm.normalizeOutputQuality(value.quality, route.workflow),
            resumeIdentity,
            detailAvailable: Boolean(value.detailAvailable !== false && resumeIdentity),
            aggregateOnly: value.aggregateOnly === true,
            attachments: vm.normalizeAttachments(rawAttachments, 12).map((attachment) => Object.freeze({
                ...attachment,
                canDelete: route.scope !== 'bulk' && deletable.has(attachment.attachmentId),
            })),
        });
    };

    const normalizeHistory = (value = {}) => {
        const page = vm.positiveInteger(value.page, 1, 1000);
        const totalPages = Math.max(page, vm.positiveInteger(value.totalPages, 1, 1000));
        const requestedPageSize = vm.positiveInteger(value.perPage, 5, 48);
        const rawStatus = vm.cleanText(value.status, 32).toLowerCase();
        const status = rawStatus === 'all' || RUN_STATUSES.has(rawStatus) ? rawStatus : 'all';
        const statusCounts = Object.fromEntries(Object.entries(
            value.statusCounts && typeof value.statusCounts === 'object' && !Array.isArray(value.statusCounts)
                ? value.statusCounts
                : {}
        ).filter(([key]) => RUN_STATUSES.has(key)).map(([key, count]) => [
            key,
            vm.nonNegativeInteger(count, 0, 100000),
        ]));
        return Object.freeze({
            loading: value.loading === true,
            error: vm.cleanText(value.error, 400),
            page,
            perPage: HISTORY_PAGE_SIZES.has(requestedPageSize) ? requestedPageSize : 5,
            totalPages,
            totalItems: vm.nonNegativeInteger(value.totalItems, 0, 100000),
            query: vm.cleanText(value.query, 200),
            status,
            statusCounts: Object.freeze(statusCounts),
            runs: (Array.isArray(value.runs) ? value.runs : []).slice(0, 50).map(normalizeRun).filter(Boolean),
        });
    };

    const historyOpenLabel = (run = {}) => {
		if (run.status === 'stopped' && !run.attachments.length) return '';
        if (['starting', 'processing', 'stopping'].includes(run.status)) return __( 'Open progress', 'fotogenic-ai' );
        if (run.status === 'uncertain') return __( 'Check status', 'fotogenic-ai' );
        if (run.status === 'paused') return __( 'Resume run', 'fotogenic-ai' );
		if (run.status === 'removed' || (['completed', 'partial', 'failed', 'stopped'].includes(run.status) && Array.isArray(run.attachments) && run.attachments.length)) {
            return __( 'View run results', 'fotogenic-ai' );
        }
		if (run.scope === 'store_queue') return __( 'Open Product queue', 'fotogenic-ai' );
		if (run.aggregateOnly || run.scope === 'bulk') return __( 'Reopen bulk run', 'fotogenic-ai' );
		if (run.status === 'failed') return __( 'Reopen setup', 'fotogenic-ai' );
        return __( 'Reopen run', 'fotogenic-ai' );
    };

    const historyRange = (history, visibleCount) => {
        if (!history.totalItems) return __( '0 saved runs', 'fotogenic-ai' );
        const start = ((history.page - 1) * history.perPage) + 1;
        const end = Math.min(history.totalItems, start + Math.max(0, visibleCount - 1));
		return sprintf( __( '%1$d-%2$d of %3$d', 'fotogenic-ai' ), start, end, history.totalItems );
    };

    const statusIcon = (status) => {
        if (['processing', 'starting'].includes(status)) {
            const icon = icons.create('arrows-clockwise', { size: 17 });
			icon.classList.add('fotogenic-ai-icon--spin');
            return icon;
        }
        if (status === 'completed') return icons.create('check-circle', { weight: 'fill', size: 17 });
        if (status === 'removed') return icons.create('trash', { size: 17 });
        if (['uncertain', 'partial', 'failed'].includes(status)) return icons.create('warning-circle', { weight: 'fill', size: 17 });
        if (status === 'paused') return icons.create('pause-circle', { weight: 'fill', size: 17 });
        if (['stopping', 'stopped'].includes(status)) return icons.create('stop-circle', { weight: 'fill', size: 17 });
        return icons.create('clock-counter-clockwise', { size: 17 });
    };

    const statusTone = (status) => {
        if (['starting', 'processing'].includes(status)) return 'active';
        if (['stopping', 'removed'].includes(status)) return 'stopped';
        return status;
    };

    const createHistoryView = ({ mount, announce = () => {} } = {}) => {
        let current = null;
        let handlers = {};
        let pending = '';
        let actionError = '';
        let opened = false;
        let query = '';
        let statusFilter = 'all';
        let filterTimer = 0;
        let restoreFocusKey = '';
        let destroyed = false;

        const requestFilter = ({ immediate = false } = {}) => {
            if (filterTimer) {
                clearTimeout(filterTimer);
                filterTimer = 0;
            }
            const request = () => {
                filterTimer = 0;
                if (destroyed || typeof handlers.filter !== 'function') return;
                void handlers.filter({
                    page: 1,
                    perPage: current?.perPage || 5,
                    query,
                    status: statusFilter,
                });
            };
            if (immediate) {
                request();
                return;
            }
            filterTimer = setTimeout(request, 300);
        };

        const invoke = async (name, callback, payload, { navigation = false } = {}) => {
            if (destroyed || pending || typeof callback !== 'function') return false;
            pending = name;
            actionError = '';
            renderCurrent();
            let navigated = false;
            try {
                const outcome = await callback(payload);
                navigated = navigation && outcome !== false;
                if (navigated) {
                    restoreFocusKey = '';
                }
            } catch (_) {
                actionError = __( 'History could not be updated. Try refreshing this view.', 'fotogenic-ai' );
                announce(actionError);
            } finally {
                pending = '';
                if (!destroyed && !navigated) {
                    renderCurrent();
                }
            }
            return navigated;
        };

        const actionButton = (label, name, callback, payload, options = {}) => {
            const control = historyButton(
                pending === name ? sprintf( __( '%s…', 'fotogenic-ai' ), label ) : label,
                options.className || ''
            );
            control.setAttribute('data-fa-focus-key', `history:${options.focusKey || name}`);
            control.disabled = Boolean(pending);
            control.setAttribute('aria-busy', pending === name ? 'true' : 'false');
            control.addEventListener('click', () => void invoke(name, callback, payload, options));
            return control;
        };

        const renderRun = (run) => {
            const row = document.createElement('tr');
            const runCell = document.createElement('td');
            runCell.append(
                vm.element('strong', '', run.label),
                vm.element('small', '', run.createdLabel || __( 'Date unavailable', 'fotogenic-ai' ))
            );

            const workflowCell = document.createElement('td');
            workflowCell.append(
                vm.element('strong', '', labels[run.workflow]),
                vm.element('small', '', sprintf(
                    __( '%1$s · %2$s', 'fotogenic-ai' ),
					run.scope === 'store_queue' ? __( 'Product queue', 'fotogenic-ai' ) : (run.scope === 'bulk' ? __( 'Bulk', 'fotogenic-ai' ) : __( 'Single', 'fotogenic-ai' )),
                    SOURCE_LABELS[run.sourceType]
                ))
            );

            const itemsCell = vm.element('td', '', String(run.itemCount));
            itemsCell.dataset.label = __( 'Items', 'fotogenic-ai' );
            const outputsCell = vm.element('td', '', String(run.totalOutputs));
            outputsCell.dataset.label = __( 'Outputs', 'fotogenic-ai' );
            const statusCell = document.createElement('td');
            const toneClass = `is-${statusTone(run.status)}`;
            const status = vm.element('span', style('history-status', ...(historyStyles[toneClass] ? [toneClass] : [])));
            status.append(
                statusIcon(run.status),
                vm.STATUS_LABELS[run.status] || __( 'Status unavailable', 'fotogenic-ai' )
            );
            statusCell.appendChild(status);

            const actionCell = document.createElement('td');
			const openLabel = historyOpenLabel(run);
			if (typeof handlers.openRun === 'function' && run.resumeIdentity && openLabel) {
				const open = actionButton(
					openLabel,
                    `open:${run.runId}`,
                    handlers.openRun,
                    run,
                    {
                        className: style('history-row-action'),
                        focusKey: `open:${encodeURIComponent(run.runId)}`,
                        navigation: true,
                    }
                );
                open.appendChild(icons.create('arrow-right', { size: 14 }));
                open.setAttribute('aria-label', sprintf(
                    __( '%1$s for %2$s', 'fotogenic-ai' ),
					openLabel,
                    run.label
                ));
                actionCell.appendChild(open);
			} else {
				actionCell.appendChild(vm.element('span', style('history-unavailable'), run.status === 'stopped'
					? __( 'No saved results', 'fotogenic-ai' )
					: __( 'Details unavailable', 'fotogenic-ai' )));
            }
            row.append(runCell, workflowCell, itemsCell, outputsCell, statusCell, actionCell);
            return row;
        };

        const renderCurrent = () => {
            if (!mount || !current || destroyed) return null;
            const active = document.activeElement;
            restoreFocusKey = active && mount.contains?.(active) === true
                ? active.getAttribute?.('data-fa-focus-key') || restoreFocusKey
                : restoreFocusKey;

            const state = vm.element('main', style('run-history-workspace'));
            state.setAttribute('data-fa-run-history', '');
            state.setAttribute('aria-busy', pending || current.loading ? 'true' : 'false');
            const header = vm.element('header', style('run-history-heading'));
            const headerCopy = vm.element('div');
            const heading = vm.element('h1', '', __( 'Run history', 'fotogenic-ai' ));
            heading.tabIndex = -1;
            const subtitle = vm.element('p');
            subtitle.append(
                vm.element('span', '', __( 'Reopen results, continue paused work, or inspect a previous generation run.', 'fotogenic-ai' )),
                vm.element('span', '', sprintf(
                    _n( '%d saved run', '%d saved runs', current.totalItems, 'fotogenic-ai' ),
                    current.totalItems
                ))
            );
            headerCopy.append(heading, subtitle);
            header.appendChild(headerCopy);
            if (typeof handlers.back === 'function') {
                const back = actionButton(
                    handlers.backLabel || __( 'Back to Summary', 'fotogenic-ai' ),
                    'back',
                    handlers.back,
                    current,
                    { className: style('history-back'), focusKey: 'back', navigation: true }
                );
                back.prepend(icons.create('arrow-left', { size: 15 }));
                header.appendChild(back);
            }
            state.appendChild(header);

            const controls = vm.element('div', style('run-history-controls'));
            controls.setAttribute('aria-label', __( 'Run history filters', 'fotogenic-ai' ));
            const searchLabel = vm.element('label', style('history-search'));
            searchLabel.appendChild(icons.create('magnifying-glass', { size: 17 }));
            searchLabel.appendChild(vm.element('span', 'fotogenic-ai-studio-sr-only', __( 'Search run history', 'fotogenic-ai' )));
            const search = document.createElement('input');
            search.value = query;
            search.placeholder = __( 'Search runs or sources', 'fotogenic-ai' );
            search.disabled = Boolean(pending);
            search.setAttribute('data-fa-focus-key', 'history:search');
            search.addEventListener('input', () => {
                query = vm.cleanText(search.value, 200);
                requestFilter();
            });
            searchLabel.appendChild(search);

            const filterLabel = vm.element('label', style('history-filter'));
            filterLabel.appendChild(vm.element('span', 'fotogenic-ai-studio-sr-only', __( 'Run status', 'fotogenic-ai' )));
            const filter = document.createElement('select');
            filter.disabled = Boolean(pending);
            filter.setAttribute('data-fa-focus-key', 'history:filter');
            const allStatusCount = Object.values(current.statusCounts).reduce((total, count) => total + count, 0);
            [['all', sprintf( __( 'All statuses · %d', 'fotogenic-ai' ), allStatusCount )], ...Object.entries(current.statusCounts).map(([value, count]) => [
                value,
                sprintf( __( '%1$s · %2$d', 'fotogenic-ai' ), vm.STATUS_LABELS[value] || value, count ),
            ])].forEach(([value, label]) => {
                const option = document.createElement('option');
                option.value = value;
                option.textContent = label;
                option.selected = statusFilter === value;
                filter.appendChild(option);
            });
            filter.addEventListener('change', () => {
                statusFilter = filter.value;
                requestFilter({ immediate: true });
            });
            filterLabel.append(filter, icons.create('caret-down', { weight: 'bold', size: 14 }));
            controls.append(searchLabel, filterLabel);
            state.appendChild(controls);

            const hasFilters = Boolean(query.trim()) || statusFilter !== 'all';
            const ledger = vm.element('section', style('run-history-ledger'));
            ledger.setAttribute('aria-label', __( 'Saved runs', 'fotogenic-ai' ));
            if (current.loading && !current.runs.length) {
                const loading = vm.element('p', style('run-history-empty'), __( 'Loading generation history…', 'fotogenic-ai' ));
                loading.setAttribute('role', 'status');
                ledger.appendChild(loading);
            } else if (current.error) {
                ledger.appendChild(vm.element('p', style('run-history-empty', 'history-error'), current.error));
            } else {
                const scroll = vm.element('div', style('run-history-table-scroll'));
                scroll.tabIndex = 0;
                scroll.setAttribute('role', 'region');
                scroll.setAttribute('aria-label', __( 'Saved runs table', 'fotogenic-ai' ));
                const table = vm.element('table', style('run-history-table'));
                const caption = vm.element('caption', 'fotogenic-ai-studio-sr-only', __( 'Saved generation runs', 'fotogenic-ai' ));
                const head = document.createElement('thead');
                const headerRow = document.createElement('tr');
                [__( 'Run', 'fotogenic-ai' ), __( 'Workflow', 'fotogenic-ai' ), __( 'Items', 'fotogenic-ai' ), __( 'Outputs', 'fotogenic-ai' ), __( 'Status', 'fotogenic-ai' )]
                    .forEach((label) => headerRow.appendChild(vm.element('th', '', label)));
                const actionHeading = vm.element('th');
                actionHeading.appendChild(vm.element('span', 'fotogenic-ai-studio-sr-only', __( 'Action', 'fotogenic-ai' )));
                headerRow.appendChild(actionHeading);
                head.appendChild(headerRow);
                const body = document.createElement('tbody');
                current.runs.forEach((run) => body.appendChild(renderRun(run)));
                table.append(caption, head, body);
                scroll.appendChild(table);
                if (!current.runs.length) {
                    const empty = vm.element('div', style('run-history-empty'));
                    empty.append(
                        icons.create('clock-counter-clockwise', { size: 26 }),
                        vm.element('strong', '', hasFilters ? __( 'No matching runs', 'fotogenic-ai' ) : __( 'No saved runs yet', 'fotogenic-ai' )),
                        vm.element('p', '', hasFilters
                            ? __( 'Try another search or include more statuses.', 'fotogenic-ai' )
                            : __( 'Completed and recoverable runs will appear here.', 'fotogenic-ai' ))
                    );
                    scroll.appendChild(empty);
                }
                ledger.appendChild(scroll);
            }

            const pagination = vm.element('footer', style('run-history-pagination'));
            pagination.setAttribute('data-studio-workspace-footer', '');
            const sizeLabel = vm.element('label');
            sizeLabel.appendChild(vm.element('span', '', __( 'Rows per page', 'fotogenic-ai' )));
            const pageSize = document.createElement('select');
            pageSize.disabled = Boolean(pending);
            pageSize.setAttribute('data-fa-focus-key', 'history:page-size');
            [5, 10, 25].forEach((size) => {
                const option = document.createElement('option');
                option.value = String(size);
                option.textContent = String(size);
                option.selected = current.perPage === size;
                pageSize.appendChild(option);
            });
            pageSize.addEventListener('change', () => {
                const perPage = Number(pageSize.value);
                if (typeof handlers.pageSize === 'function') {
                    void invoke('page-size', handlers.pageSize, { page: 1, perPage, query, status: statusFilter });
                }
            });
            sizeLabel.append(pageSize, icons.create('caret-down', { weight: 'bold', size: 13 }));
            const range = vm.element('span', '', historyRange(current, current.runs.length));
            range.setAttribute('role', 'status');
            range.setAttribute('aria-live', 'polite');
            const pager = vm.element('div');
            const previous = actionButton(
                __( 'Previous', 'fotogenic-ai' ),
                'previous',
                handlers.page,
                { page: current.page - 1, perPage: current.perPage, query, status: statusFilter },
                { focusKey: 'previous' }
            );
            const next = actionButton(
                __( 'Next', 'fotogenic-ai' ),
                'next',
                handlers.page,
                { page: current.page + 1, perPage: current.perPage, query, status: statusFilter },
                { focusKey: 'next' }
            );
            previous.disabled = Boolean(pending) || typeof handlers.page !== 'function' || current.page <= 1;
            next.disabled = Boolean(pending) || typeof handlers.page !== 'function' || current.page >= current.totalPages;
            previous.replaceChildren(icons.create('arrow-left', { size: 15 }));
            previous.setAttribute('aria-label', __( 'Previous history page', 'fotogenic-ai' ));
            next.replaceChildren(icons.create('arrow-right', { size: 15 }));
            next.setAttribute('aria-label', __( 'Next history page', 'fotogenic-ai' ));
            const pagePosition = vm.element('strong', '', `${current.page} / ${current.totalPages}`);
            pagePosition.setAttribute('aria-label', sprintf( __( 'Page %1$d of %2$d', 'fotogenic-ai' ), current.page, current.totalPages ));
            pager.append(sizeLabel, previous, pagePosition, next);
            pagination.append(range, pager);
            ledger.appendChild(pagination);
            state.appendChild(ledger);

            if (actionError) state.appendChild(vm.element('p', style('history-error'), actionError));
            mount.replaceChildren(state);
            const restoreTarget = [...mount.querySelectorAll('[data-fa-focus-key]')]
                .find((node) => node.getAttribute?.('data-fa-focus-key') === restoreFocusKey);
            if (restoreTarget?.focus && !restoreTarget.disabled && !restoreTarget.hidden) {
                restoreTarget.focus({ preventScroll: true });
                if (restoreTarget === search) search.setSelectionRange?.(search.value.length, search.value.length);
                restoreFocusKey = '';
            } else if (restoreFocusKey && !pending) {
                restoreFocusKey = '';
                heading.focus({ preventScroll: true });
            }
            if (!opened) {
                opened = true;
                vm.focusHeading(mount);
                announce(__( 'Run history.', 'fotogenic-ai' ));
            }
            return current;
        };

        const render = (value, nextHandlers = {}, options = {}) => {
            actionError = '';
            current = normalizeHistory(value);
            handlers = nextHandlers && typeof nextHandlers === 'object' ? nextHandlers : {};
            if (options.reset === true) {
                query = '';
                statusFilter = 'all';
            } else {
                query = current.query;
                statusFilter = current.status;
            }
            if (options.focus === true) opened = false;
            return renderCurrent();
        };

        return Object.freeze({
            render,
            destroy() {
                destroyed = true;
                if (filterTimer) clearTimeout(filterTimer);
                filterTimer = 0;
                mount?.replaceChildren?.();
                current = null;
                handlers = {};
            },
        });
    };

    Studio.history = Object.freeze({
        createHistoryView,
        normalizeHistory,
        normalizeRun,
        historyOpenLabel,
        historyRange,
    });
})();
