(function () {
    const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
    const pollingDelayMs = 3000;
    let pollingTimer = null;
    let selectedRun = null;

    function text(value, fallback = '') {
        if (value === null || value === undefined || value === '') {
            return fallback;
        }
        return String(value);
    }

    function escapeHtml(value) {
        return text(value)
            .replace(/&/g, '&amp;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;')
            .replace(/"/g, '&quot;')
            .replace(/'/g, '&#39;');
    }

    function showAlert(message, type = 'danger') {
        const container = document.getElementById('dynamic-plan-alert');
        container.innerHTML = message
            ? `<div class="alert alert-${type} mb-3" role="alert">${escapeHtml(message)}</div>`
            : '';
    }

    function parseSatelliteIds(value) {
        return String(value || '')
            .split(/[,\s;]+/)
            .map(item => item.trim())
            .filter(Boolean)
            .map(item => Number(item))
            .filter(item => Number.isInteger(item));
    }

    function formatDuration(value) {
        const ms = Number(value);
        if (!Number.isFinite(ms) || ms < 0) {
            return '-';
        }
        if (ms < 1000) {
            return `${Math.round(ms)} мс`;
        }
        const seconds = Math.round(ms / 1000);
        const minutes = Math.floor(seconds / 60);
        const remainder = seconds % 60;
        return minutes > 0 ? `${minutes} мин ${remainder} с` : `${remainder} с`;
    }

    function formatDateTime(value) {
        return value ? PcpUtcTime.formatDateTime(value) : '-';
    }

    function startOfDay(value) {
        return `${value}T00:00:00`;
    }

    function endOfDay(value) {
        return `${value}T23:59:59`;
    }

    function shortId(value) {
        const fullValue = text(value, '-');
        return fullValue.length > 12 ? `${fullValue.slice(0, 8)}...${fullValue.slice(-4)}` : fullValue;
    }

    function statusText(status) {
        switch (status) {
            case 'PENDING':
                return 'В очереди';
            case 'RUNNING':
                return 'Выполняется';
            case 'COMPLETED':
                return 'Готово';
            case 'FAILED':
                return 'Ошибка';
            default:
                return text(status, '-');
        }
    }

    function calculationModeText(mode) {
        switch (mode) {
            case 'GREEDY':
                return 'Жадный';
            case 'FULL':
                return 'Полный';
            default:
                return text(mode, '-');
        }
    }

    function revolutionModeText(mode) {
        switch (mode) {
            case 'ASC':
                return 'Восходящий';
            case 'DESC':
                return 'Нисходящий';
            case 'BOTH':
                return 'Оба';
            default:
                return text(mode, '-');
        }
    }

    function booleanText(value) {
        return value ? 'Да' : 'Нет';
    }

    function percentText(value) {
        const percent = Number(value);
        return Number.isFinite(percent) ? `${percent.toFixed(2)}%` : '-';
    }

    function routeIntervalText(start, end) {
        if (!start && !end) {
            return '-';
        }
        return `${formatDateTime(start)} - ${formatDateTime(end)}`;
    }

    function renderRuns(items) {
        const body = document.getElementById('dynamic-plan-runs-body');
        const meta = document.getElementById('dynamic-plan-runs-meta');
        const runs = Array.isArray(items) ? items : [];
        meta.textContent = runs.length > 0 ? `Показано запусков: ${runs.length}` : 'Запуски не найдены.';

        if (runs.length === 0) {
            body.innerHTML = '<div class="text-muted py-4 text-center">Нет данных</div>';
            return;
        }

        body.innerHTML = runs.map(run => `
            <button type="button" class="dynamic-plan-run-item" data-run-id="${escapeHtml(run.runId)}">
                <span class="dynamic-plan-run-main">
                    <span class="dynamic-plan-run-title">${escapeHtml(shortId(run.runId))}</span>
                    <span class="dynamic-plan-run-details d-block">
                        ${escapeHtml(formatDateTime(run.createdAt))} · ${escapeHtml(calculationModeText(run.calculationMode))} · ${escapeHtml(revolutionModeText(run.revolutionMode))} · фильтр ${escapeHtml(booleanText(run.filterCoveredRoutes))} · заявка ${escapeHtml(shortId(run.requestId))} · маршруты ${escapeHtml(run.routesCount ?? '-')}
                    </span>
                </span>
                <span class="dynamic-plan-run-status">${escapeHtml(statusText(run.status))}</span>
            </button>
        `).join('');

        body.querySelectorAll('[data-run-id]').forEach(button => {
            button.addEventListener('click', () => openRun(button.dataset.runId));
        });
    }

    async function loadRuns() {
        const payload = await requestJson('/api/dynamic-plan/runs?limit=20&offset=0', {
            method: 'GET',
            headers: {
                'Accept': 'application/json'
            }
        });
        renderRuns(payload);
    }

    async function requestJson(url, options) {
        const response = await fetch(url, options);
        const isJson = response.headers.get('content-type')?.includes('application/json');
        const body = isJson ? await response.json() : await response.text();

        if (!response.ok) {
            if (body && typeof body === 'object') {
                throw new Error(body.error || body.message || Object.values(body).join('; '));
            }
            throw new Error(body || `HTTP ${response.status}`);
        }

        return body;
    }

    function field(label, value) {
        return `
            <div class="dynamic-plan-field">
                <div class="dynamic-plan-field-label">${escapeHtml(label)}</div>
                <div class="dynamic-plan-field-value">${escapeHtml(value)}</div>
            </div>
        `;
    }

    function mapControls(run) {
        if (!run || run.status !== 'COMPLETED') {
            return '';
        }

        return `
            <div class="dynamic-plan-map-controls mb-4">
                <div>
                    <div class="dynamic-plan-section-title">Карта</div>
                    <div id="dynamic-plan-map-state" class="dynamic-plan-helper mt-1"></div>
                </div>
                <div class="dynamic-plan-map-actions">
                    <button id="dynamic-plan-map-show" type="button" class="btn btn-outline-primary btn-sm">Отобразить расчет</button>
                    <button id="dynamic-plan-map-hide" type="button" class="btn btn-outline-secondary btn-sm">Скрыть расчет</button>
                    <button id="dynamic-plan-map-open" type="button" class="btn btn-primary btn-sm">Открыть карту</button>
                </div>
            </div>
        `;
    }

    function updateMapControls(run) {
        const showButton = document.getElementById('dynamic-plan-map-show');
        const hideButton = document.getElementById('dynamic-plan-map-hide');
        const state = document.getElementById('dynamic-plan-map-state');
        if (!showButton || !hideButton || !state || !window.PcpDynamicPlanMapLayers) {
            return;
        }

        const visible = window.PcpDynamicPlanMapLayers.isRunVisible(run.runId);
        showButton.disabled = visible;
        hideButton.disabled = !visible;
        state.textContent = visible
            ? 'Расчет добавлен в слой карты.'
            : 'Расчет не отображается на карте.';
    }

    function attachMapControls(run) {
        const showButton = document.getElementById('dynamic-plan-map-show');
        const hideButton = document.getElementById('dynamic-plan-map-hide');
        const openButton = document.getElementById('dynamic-plan-map-open');
        if (!showButton || !hideButton || !openButton || !window.PcpDynamicPlanMapLayers) {
            return;
        }

        showButton.addEventListener('click', () => {
            window.PcpDynamicPlanMapLayers.showRun(run);
            updateMapControls(run);
            showAlert('Расчет добавлен в слой карты. Откройте вкладку map для просмотра.', 'success');
        });
        hideButton.addEventListener('click', () => {
            window.PcpDynamicPlanMapLayers.hideRun(run.runId);
            updateMapControls(run);
            showAlert('Расчет скрыт с карты.', 'success');
        });
        openButton.addEventListener('click', () => {
            window.location.href = '/map';
        });
        updateMapControls(run);
    }

    function clearPolling() {
        if (pollingTimer) {
            clearTimeout(pollingTimer);
            pollingTimer = null;
        }
    }

    function renderRun(payload) {
        selectedRun = payload;
        const result = document.getElementById('dynamic-plan-result');
        const meta = document.getElementById('dynamic-plan-result-meta');
        meta.textContent = `Статус запуска: ${statusText(payload.status)}`;
        result.innerHTML = `
            <div class="dynamic-plan-result-grid mb-4">
                ${field('Run ID', payload.runId)}
                ${field('Статус запуска', statusText(payload.status))}
                ${field('Вариант расчета', calculationModeText(payload.calculationMode))}
                ${field('Виток', revolutionModeText(payload.revolutionMode))}
                ${field('Фильтр покрытия', booleanText(payload.filterCoveredRoutes))}
                ${field('Заявка', payload.requestId)}
                ${field('КА', Array.isArray(payload.satelliteIds) ? payload.satelliteIds.join(', ') : '-')}
                ${field('Начало интервала', formatDateTime(payload.calculationStart))}
                ${field('Конец интервала', formatDateTime(payload.calculationEnd))}
                ${field('Статус результата', payload.resultStatus ?? '-')}
                ${field('Маршруты', payload.routesCount ?? '-')}
                ${field('Длительность', formatDuration(payload.durationMs))}
            </div>
            ${payload.errorMessage ? `<div class="alert alert-danger mb-4" role="alert">${escapeHtml(payload.errorMessage)}</div>` : ''}
            ${mapControls(payload)}
            <div class="dynamic-plan-section-title mb-2">Run payload</div>
            <pre class="dynamic-plan-json mb-0">${escapeHtml(JSON.stringify(payload, null, 2))}</pre>
        `;
        attachMapControls(payload);
    }

    function renderResult(payload, runId) {
        const run = selectedRun && String(selectedRun.runId) === String(runId)
            ? {
                ...selectedRun,
                status: 'COMPLETED',
                requestId: payload.requestId ?? selectedRun.requestId,
                routesCount: payload.routesCount ?? selectedRun.routesCount,
                calculationMode: payload.calculationMode ?? selectedRun.calculationMode,
                revolutionMode: payload.revolutionMode ?? selectedRun.revolutionMode,
                filterCoveredRoutes: payload.filterCoveredRoutes ?? selectedRun.filterCoveredRoutes,
                lastRouteStart: payload.lastRouteStart ?? selectedRun.lastRouteStart,
                lastRouteEnd: payload.lastRouteEnd ?? selectedRun.lastRouteEnd,
                coveredAreaPercent: payload.coveredAreaPercent ?? selectedRun.coveredAreaPercent
            }
            : {
                runId,
                status: 'COMPLETED',
                requestId: payload.requestId,
                routesCount: payload.routesCount,
                calculationMode: payload.calculationMode,
                revolutionMode: payload.revolutionMode,
                filterCoveredRoutes: payload.filterCoveredRoutes,
                lastRouteStart: payload.lastRouteStart,
                lastRouteEnd: payload.lastRouteEnd,
                coveredAreaPercent: payload.coveredAreaPercent
            };
        selectedRun = run;
        const result = document.getElementById('dynamic-plan-result');
        const meta = document.getElementById('dynamic-plan-result-meta');
        meta.textContent = `Расчет ${runId}: готов`;
        result.innerHTML = `
            <div class="dynamic-plan-result-grid mb-4">
                ${field('Run ID', runId)}
                ${field('Статус результата', payload.status)}
                ${field('Вариант расчета', calculationModeText(payload.calculationMode))}
                ${field('Виток', revolutionModeText(payload.revolutionMode))}
                ${field('Фильтр покрытия', booleanText(payload.filterCoveredRoutes))}
                ${field('Заявка', payload.requestId)}
                ${field('КА', Array.isArray(payload.satelliteIds) ? payload.satelliteIds.join(', ') : '-')}
                ${field('Отсутствующие КА', Array.isArray(payload.missingSatelliteIds) && payload.missingSatelliteIds.length ? payload.missingSatelliteIds.join(', ') : '-')}
                ${field('Маршруты', payload.routesCount ?? '-')}
                ${field('Последний маршрут', routeIntervalText(payload.lastRouteStart, payload.lastRouteEnd))}
                ${field('Процент снятой территории', percentText(payload.coveredAreaPercent))}
                ${field('Длительность', formatDuration(payload.durationMs))}
            </div>
            ${mapControls(run)}
            <div class="dynamic-plan-section-title mb-2">Result payload</div>
            <pre class="dynamic-plan-json mb-0">${escapeHtml(JSON.stringify(payload, null, 2))}</pre>
        `;
        attachMapControls(run);
    }

    function setSubmitLoading(isLoading) {
        const button = document.getElementById('dynamic-plan-submit');
        if (!button.dataset.defaultText) {
            button.dataset.defaultText = button.innerHTML;
        }
        button.disabled = isLoading;
        button.innerHTML = isLoading
            ? '<span class="spinner-border spinner-border-sm me-1" role="status" aria-hidden="true"></span>Запуск'
            : button.dataset.defaultText;
    }

    async function loadResult(runId) {
        const payload = await requestJson(`/api/dynamic-plan/runs/${encodeURIComponent(runId)}/result`, {
            method: 'GET',
            headers: {
                'Accept': 'application/json'
            }
        });
        renderResult(payload, runId);
        showAlert('Расчет завершен, результат получен', 'success');
    }

    async function openRun(runId) {
        if (!runId) {
            return;
        }

        clearPolling();
        showAlert('');
        try {
            const payload = await requestJson(`/api/dynamic-plan/runs/${encodeURIComponent(runId)}`, {
                method: 'GET',
                headers: {
                    'Accept': 'application/json'
                }
            });
            renderRun(payload);

            if (payload.status === 'COMPLETED') {
                await loadResult(runId);
                return;
            }
            if (payload.status === 'FAILED') {
                showAlert(`Расчет завершился с ошибкой: ${payload.errorMessage || 'неизвестная ошибка'}`);
                return;
            }

            pollingTimer = setTimeout(() => pollRun(runId), pollingDelayMs);
        } catch (error) {
            showAlert(`Ошибка получения статуса расчета: ${error.message}`);
        }
    }

    async function pollRun(runId) {
        try {
            const payload = await requestJson(`/api/dynamic-plan/runs/${encodeURIComponent(runId)}`, {
                method: 'GET',
                headers: {
                    'Accept': 'application/json'
                }
            });
            renderRun(payload);

            if (payload.status === 'COMPLETED') {
                clearPolling();
                await loadResult(runId);
                loadRuns().catch(() => undefined);
                return;
            }
            if (payload.status === 'FAILED') {
                clearPolling();
                loadRuns().catch(() => undefined);
                showAlert(`Расчет завершился с ошибкой: ${payload.errorMessage || 'неизвестная ошибка'}`);
                return;
            }

            pollingTimer = setTimeout(() => pollRun(runId), pollingDelayMs);
        } catch (error) {
            clearPolling();
            showAlert(`Ошибка получения статуса расчета: ${error.message}`);
        }
    }

    async function submitForm(event) {
        event.preventDefault();
        const requestId = document.getElementById('dynamic-plan-request-id').value.trim();
        const satelliteIds = parseSatelliteIds(document.getElementById('dynamic-plan-satellite-ids').value);
        const calculationMode = document.querySelector('input[name="dynamic-plan-mode"]:checked')?.value || 'FULL';
        const revolutionMode = document.querySelector('input[name="dynamic-plan-revolution-mode"]:checked')?.value || 'BOTH';
        const filterCoveredRoutes = document.getElementById('dynamic-plan-filter-covered-routes').checked;
        const calculationStartDate = document.getElementById('dynamic-plan-interval-start').value;
        const calculationEndDate = document.getElementById('dynamic-plan-interval-end').value;

        if (!uuidPattern.test(requestId)) {
            showAlert('Укажите корректный UUID заявки');
            return;
        }
        if (satelliteIds.length === 0) {
            showAlert('Укажите хотя бы один идентификатор КА');
            return;
        }
        if (!calculationStartDate || !calculationEndDate) {
            showAlert('Укажите дату начала и дату конца интервала расчета');
            return;
        }
        if (calculationStartDate > calculationEndDate) {
            showAlert('Дата начала интервала расчета должна быть не позже даты окончания');
            return;
        }

        const calculationStart = startOfDay(calculationStartDate);
        const calculationEnd = endOfDay(calculationEndDate);

        setSubmitLoading(true);
        clearPolling();
        showAlert('');
        try {
            const payload = await requestJson('/api/dynamic-plan/calc', {
                method: 'POST',
                headers: {
                    'Accept': 'application/json',
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({
                    requestId,
                    satelliteIds,
                    calculationMode,
                    revolutionMode,
                    filterCoveredRoutes,
                    calculationStart,
                    calculationEnd
                })
            });
            renderRun(payload);
            showAlert('Расчет поставлен в очередь', 'success');
            loadRuns().catch(() => undefined);
            pollingTimer = setTimeout(() => pollRun(payload.runId), pollingDelayMs);
        } catch (error) {
            showAlert(`Ошибка запуска расчета: ${error.message}`);
        } finally {
            setSubmitLoading(false);
        }
    }

    document.getElementById('dynamic-plan-form').addEventListener('submit', submitForm);
    document.getElementById('dynamic-plan-runs-refresh').addEventListener('click', () => {
        loadRuns().catch(error => showAlert(`Ошибка обновления истории запусков: ${error.message}`));
    });
    loadRuns().catch(error => showAlert(`Ошибка загрузки истории запусков: ${error.message}`));
})();
