(function () {
    const apiBase = '/api/com-plan/runs';
    const state = {
        runs: [],
        selectedRunId: null,
        selectedRun: null,
        selectedRunModes: null,
        selectedRunModesError: null,
        page: 1,
        pageSize: 10
    };

    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('complex-plan-alert');
        container.innerHTML = message
            ? `<div class="alert alert-${type} mb-3" role="alert">${escapeHtml(message)}</div>`
            : '';
    }

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

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

    function formatList(values) {
        return Array.isArray(values) && values.length ? values.join(', ') : '-';
    }

    function formatNumber(value) {
        const number = Number(value);
        if (!Number.isFinite(number)) return '-';
        return number.toLocaleString('ru-RU', { maximumFractionDigits: 4 });
    }

    function formatPercent(value) {
        const number = Number(value);
        if (!Number.isFinite(number)) return '-';
        return `${number.toLocaleString('ru-RU', { maximumFractionDigits: 2 })}%`;
    }

    function isoDateTime(value) {
        if (!value) return '';
        const date = PcpUtcTime.parseApiDate(value);
        return date ? date.toISOString() : String(value);
    }

    function formatDateTimeLocal(value) {
        return PcpUtcTime.toLocalInputValue(value);
    }

    async function requestJson(url, options) {
        const response = await fetch(url, options);
        if (response.status === 204) {
            return null;
        }

        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 renderRuns() {
        const body = document.getElementById('complex-plan-runs-body');
        const meta = document.getElementById('complex-plan-list-meta');
        const pagination = document.getElementById('complex-plan-pagination');

        if (!state.runs.length) {
            body.innerHTML = '<tr><td colspan="5" class="text-center text-muted py-5">Расчеты не найдены</td></tr>';
            meta.textContent = 'Нет расчетов';
            pagination.innerHTML = '';
            return;
        }

        const totalPages = Math.max(1, Math.ceil(state.runs.length / state.pageSize));
        state.page = Math.min(Math.max(1, state.page), totalPages);
        const pageStart = (state.page - 1) * state.pageSize;
        const pageRuns = state.runs.slice(pageStart, pageStart + state.pageSize);

        meta.textContent = `Всего расчетов: ${state.runs.length}`;
        body.innerHTML = pageRuns.map(run => {
            const runId = run.runId ?? run.id;
            const activeClass = String(runId) === String(state.selectedRunId) ? ' active' : '';
            const interval = `${formatDateTime(run.intervalStart)} - ${formatDateTime(run.intervalEnd)}`;
            return `
                <tr class="complex-plan-run-row${activeClass}" data-run-id="${escapeHtml(runId)}">
                    <td>${escapeHtml(runId)}</td>
                    <td>${escapeHtml(run.status)}</td>
                    <td>${escapeHtml(interval)}</td>
                    <td>${escapeHtml(formatList(run.satelliteIds))}</td>
                    <td>
                        <button type="button"
                                class="btn btn-outline-danger btn-sm complex-plan-delete-btn"
                                data-delete-run-id="${escapeHtml(runId)}">
                            Удалить
                        </button>
                    </td>
                </tr>
            `;
        }).join('');

        body.querySelectorAll('.complex-plan-run-row').forEach(row => {
            row.addEventListener('click', () => selectRun(row.dataset.runId));
        });
        body.querySelectorAll('[data-delete-run-id]').forEach(button => {
            button.addEventListener('click', event => {
                event.stopPropagation();
                deleteRun(button.dataset.deleteRunId);
            });
        });

        pagination.innerHTML = `
            <button type="button" class="btn btn-outline-secondary btn-sm" id="complex-plan-page-prev" ${state.page <= 1 ? 'disabled' : ''}>
                Назад
            </button>
            <span class="complex-plan-helper">Страница ${state.page} из ${totalPages}</span>
            <button type="button" class="btn btn-outline-secondary btn-sm" id="complex-plan-page-next" ${state.page >= totalPages ? 'disabled' : ''}>
                Вперед
            </button>
        `;
        document.getElementById('complex-plan-page-prev')?.addEventListener('click', () => {
            state.page -= 1;
            renderRuns();
        });
        document.getElementById('complex-plan-page-next')?.addEventListener('click', () => {
            state.page += 1;
            renderRuns();
        });
    }

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

    function isSurveyMode(mode) {
        return !mode.type || String(mode.type).toUpperCase() === 'SURVEY';
    }

    function isAddedSurveyMode(mode) {
        const source = String(mode.source || '').toUpperCase();
        return isSurveyMode(mode) && (source === 'COMPLAN' || source === 'MIXED');
    }

    function addedSurveyRowsBySatellite(run, modes) {
        const countsBySatelliteId = new Map();
        modes
            .filter(isAddedSurveyMode)
            .forEach(mode => {
                if (mode.satelliteId === null || mode.satelliteId === undefined || mode.satelliteId === '') {
                    return;
                }

                const key = String(mode.satelliteId);
                countsBySatelliteId.set(key, (countsBySatelliteId.get(key) || 0) + 1);
            });

        const satelliteIds = [...new Set([
            ...(Array.isArray(run.satelliteIds) ? run.satelliteIds : []),
            ...modes.map(mode => mode.satelliteId)
        ].filter(id => id !== null && id !== undefined && id !== ''))]
            .sort((left, right) => Number(left) - Number(right));

        return satelliteIds.map(satelliteId => [
            satelliteId,
            countsBySatelliteId.get(String(satelliteId)) || 0
        ]);
    }

    function renderAddedSurveyStats(run) {
        if (state.selectedRunModesError) {
            return '<div class="text-muted">Не удалось загрузить съемки расчета</div>';
        }

        if (state.selectedRunModes === null) {
            return '<div class="text-muted">Загрузка съемок...</div>';
        }

        const rows = addedSurveyRowsBySatellite(run, state.selectedRunModes);
        if (!rows.length) {
            return '<div class="text-muted">Нет данных по спутникам</div>';
        }

        return `
            <div class="complex-plan-added-surveys">
                <table class="table table-sm align-middle mb-0">
                    <thead>
                    <tr>
                        <th>Спутник</th>
                        <th class="text-end">Добавлено съемок</th>
                    </tr>
                    </thead>
                    <tbody>
                    ${rows.map(([satelliteId, count]) => `
                        <tr>
                            <td>${escapeHtml(satelliteId)}</td>
                            <td class="text-end">${escapeHtml(count)}</td>
                        </tr>
                    `).join('')}
                    </tbody>
                </table>
            </div>
        `;
    }

    function renderDetail(run) {
        const container = document.getElementById('complex-plan-detail');
        const meta = document.getElementById('complex-plan-detail-meta');

        if (!run) {
            meta.textContent = 'Выберите расчет слева.';
            container.innerHTML = '<div class="text-muted py-5 text-center">Нет выбранного расчета</div>';
            document.getElementById('complex-plan-report').disabled = true;
            return;
        }

        const runId = run.runId ?? run.id;
        meta.textContent = `Расчет #${runId}`;
        document.getElementById('complex-plan-report').disabled = false;
        const requestPayload = formatJsonText(run.requestPayload);

        container.innerHTML = `
            <div class="complex-plan-detail-grid mb-4">
                ${field('ID', runId)}
                ${field('Статус', run.status)}
                ${field('Начало интервала', formatDateTime(run.intervalStart))}
                ${field('Конец интервала', formatDateTime(run.intervalEnd))}
                ${field('Спутники', formatList(run.satelliteIds))}
                ${field('Инициатор', text(run.requestedBy, '-'))}
                ${field('Создан', formatDateTime(run.createdAt))}
                ${field('Запущен', formatDateTime(run.startedAt))}
                ${field('Завершен', formatDateTime(run.finishedAt))}
                ${field('Длительность', formatDuration(run.durationMs))}
                ${field('Ошибка', text(run.errorMessage, '-'))}
                ${field('Snapshot IDs', formatList(run.snapshotIds))}
                ${field('Calculated modes', run.calculatedModesCount ?? 0)}
                ${field('Booked modes', run.bookedModesCount ?? 0)}
                ${field('Complan modes', run.complanModesCount ?? 0)}
                ${field('Mixed modes', run.mixedModesCount ?? 0)}
                ${field('Affected satellites', run.affectedSatellitesCount ?? 0)}
                ${field('Processed cells', run.processedCellsCount ?? 0)}
                ${field('Booked slot links', run.bookedSlotLinksCount ?? 0)}
                ${field('Процент покрытия', formatPercent(run.coveredAreaPercent))}
            </div>
            <div class="complex-plan-section-title mb-2">Добавлено съемок по спутникам</div>
            <div class="mb-4">${renderAddedSurveyStats(run)}</div>
            <div class="complex-plan-section-title mb-2">Request payload</div>
            <pre class="complex-plan-json mb-0">${escapeHtml(requestPayload || '-')}</pre>
        `;
    }

    function formatJsonText(value) {
        if (!value) return '';
        if (typeof value === 'object') {
            return JSON.stringify(value, null, 2);
        }
        try {
            return JSON.stringify(JSON.parse(value), null, 2);
        } catch (error) {
            return String(value);
        }
    }

    function parseRequestPayload(value) {
        if (!value) return {};
        if (typeof value === 'object') return value;

        try {
            return JSON.parse(value);
        } catch (error) {
            return {};
        }
    }

    function fillProcessForm(run) {
        const payload = parseRequestPayload(run.requestPayload);
        const intervalStart = payload.intervalStart || run.intervalStart;
        const intervalEnd = payload.intervalEnd || run.intervalEnd;
        const satelliteIds = Array.isArray(payload.satelliteIds) && payload.satelliteIds.length
            ? payload.satelliteIds
            : (Array.isArray(run.satelliteIds) ? run.satelliteIds : []);

        document.getElementById('complex-plan-interval-start').value = formatDateTimeLocal(intervalStart);
        document.getElementById('complex-plan-interval-end').value = formatDateTimeLocal(intervalEnd);
        document.getElementById('complex-plan-satellite-ids').value = satelliteIds.join(', ');
        document.getElementById('complex-plan-sun').value = payload.sun === null || payload.sun === undefined
            ? ''
            : payload.sun;
        document.getElementById('complex-plan-count-lat').value = payload.countLat === null || payload.countLat === undefined
            ? ''
            : payload.countLat;
        document.getElementById('complex-plan-count-long').value = payload.countLong === null || payload.countLong === undefined
            ? ''
            : payload.countLong;

        const coverageSourceInput = document.getElementById('complex-plan-coverage-source');
        if (payload.coverageSource && Array.from(coverageSourceInput.options).some(option => option.value === payload.coverageSource)) {
            coverageSourceInput.value = payload.coverageSource;
        } else {
            coverageSourceInput.value = 'SLOTS';
        }

        const coverageStrategyInput = document.getElementById('complex-plan-coverage-strategy');
        if (payload.coverageStrategy && Array.from(coverageStrategyInput.options).some(option => option.value === payload.coverageStrategy)) {
            coverageStrategyInput.value = payload.coverageStrategy;
        } else {
            coverageStrategyInput.value = 'GREEDY';
        }
    }

    async function selectRun(runId) {
        if (!runId) return;

        state.selectedRunId = runId;
        state.selectedRunModes = null;
        state.selectedRunModesError = null;
        renderRuns();
        renderDetail(null);
        document.getElementById('complex-plan-detail-meta').textContent = `Загрузка расчета #${runId}...`;

        try {
            const run = await requestJson(`${apiBase}/${encodeURIComponent(runId)}`);
            state.selectedRun = run;
            renderDetail(run);
            fillProcessForm(run);
            showAlert('');

            try {
                const modes = await requestJson(`${apiBase}/${encodeURIComponent(runId)}/modes`);
                if (String(state.selectedRunId) !== String(runId)) {
                    return;
                }

                state.selectedRunModes = Array.isArray(modes) ? modes : [];
                state.selectedRunModesError = null;
            } catch (error) {
                if (String(state.selectedRunId) !== String(runId)) {
                    return;
                }

                state.selectedRunModes = [];
                state.selectedRunModesError = error;
            }
            renderDetail(run);
        } catch (error) {
            state.selectedRun = null;
            state.selectedRunModes = null;
            state.selectedRunModesError = null;
            showAlert(error.message || 'Не удалось загрузить расчет');
        }
    }

    async function deleteRun(runId) {
        if (!runId) return;
        const confirmed = window.confirm(`Удалить версию расчета #${runId}?`);
        if (!confirmed) return;

        try {
            await requestJson(`${apiBase}/${encodeURIComponent(runId)}`, { method: 'DELETE' });
            if (String(state.selectedRunId) === String(runId)) {
                state.selectedRunId = null;
                state.selectedRun = null;
                state.selectedRunModes = null;
                state.selectedRunModesError = null;
                renderDetail(null);
            }
            await loadRuns();
            showAlert(`Версия расчета #${runId} удалена`, 'success');
        } catch (error) {
            showAlert(error.message || 'Не удалось удалить расчет');
        }
    }

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

    function parseOptionalPositiveInt(value) {
        const parsedValue = Number.parseInt(String(value || '').trim(), 10);
        return Number.isFinite(parsedValue) && parsedValue > 0 ? parsedValue : null;
    }

    async function submitProcessForm(event) {
        event.preventDefault();
        const submitButton = document.getElementById('complex-plan-process-submit');
        const intervalStart = document.getElementById('complex-plan-interval-start').value;
        const intervalEnd = document.getElementById('complex-plan-interval-end').value;
        const satelliteIds = parseSatelliteIds(document.getElementById('complex-plan-satellite-ids').value);
        const sunValue = document.getElementById('complex-plan-sun').value.trim();
        const coverageSource = document.getElementById('complex-plan-coverage-source').value;
        const coverageStrategy = document.getElementById('complex-plan-coverage-strategy').value;
        const countLat = parseOptionalPositiveInt(document.getElementById('complex-plan-count-lat').value);
        const countLong = parseOptionalPositiveInt(document.getElementById('complex-plan-count-long').value);

        if (!intervalStart || !intervalEnd || satelliteIds.length === 0) {
            showAlert('Заполните интервал и список Satellite IDs');
            return;
        }

        submitButton.disabled = true;
        showAlert('');
        try {
            const response = await requestJson('/api/com-plan/process', {
                method: 'POST',
                headers: {
                    'Accept': 'application/json',
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({
                    intervalStart: PcpUtcTime.toUtcIsoFromInput(intervalStart),
                    intervalEnd: PcpUtcTime.toUtcIsoFromInput(intervalEnd),
                    satelliteId: null,
                    satelliteIds: satelliteIds,
                    sun: sunValue ? Number(sunValue) : null,
                    coverageSource: coverageSource || null,
                    countLat: countLat,
                    countLong: countLong,
                    coverageStrategy: coverageStrategy || null
                })
            });
            const runId = response?.runId ?? response?.id;
            await loadRuns(runId ? String(runId) : null);
            showAlert(`Расчет${runId ? ` #${runId}` : ''} запущен`, 'success');
        } catch (error) {
            showAlert(error.message || 'Не удалось запустить расчет');
        } finally {
            submitButton.disabled = false;
        }
    }

    async function loadRuns(preferredRunId) {
        const refreshButton = document.getElementById('complex-plan-refresh');
        refreshButton.disabled = true;
        if (!preferredRunId) {
            showAlert('');
        }
        document.getElementById('complex-plan-list-meta').textContent = 'Загрузка...';

        try {
            const runs = await requestJson(apiBase);
            state.runs = Array.isArray(runs) ? runs : [];
            const preferredId = preferredRunId || state.selectedRunId;
            const preferredRun = state.runs.find(run => String(run.runId ?? run.id) === String(preferredId));
            state.selectedRunId = preferredRun
                ? String(preferredRun.runId ?? preferredRun.id)
                : (state.runs[0] ? String(state.runs[0].runId ?? state.runs[0].id) : null);
            if (state.selectedRunId) {
                const selectedIndex = state.runs.findIndex(run => String(run.runId ?? run.id) === String(state.selectedRunId));
                state.page = Math.floor(Math.max(0, selectedIndex) / state.pageSize) + 1;
            }
            renderRuns();
            if (state.selectedRunId) {
                await selectRun(state.selectedRunId);
            } else {
                state.selectedRun = null;
                renderDetail(null);
            }
        } catch (error) {
            state.runs = [];
            renderRuns();
            state.selectedRunModes = null;
            state.selectedRunModesError = null;
            renderDetail(null);
            showAlert(error.message || 'Не удалось загрузить расчеты');
        } finally {
            refreshButton.disabled = false;
        }
    }

    function csvValue(value) {
        if (typeof value === 'number' && Number.isFinite(value)) {
            return String(value).replace('.', ',');
        }

        const normalized = text(value, '')
            .replace(/\r?\n/g, ' ')
            .replace(/"/g, '""');
        return `"${normalized}"`;
    }

    function csvRow(values) {
        return values.map(csvValue).join(';');
    }

    function runDetailRows(run) {
        return [
            ['ID', run.runId ?? run.id],
            ['Статус', run.status],
            ['Начало интервала', isoDateTime(run.intervalStart)],
            ['Конец интервала', isoDateTime(run.intervalEnd)],
            ['Спутники', formatList(run.satelliteIds)],
            ['Инициатор', text(run.requestedBy, '-')],
            ['Создан', isoDateTime(run.createdAt)],
            ['Запущен', isoDateTime(run.startedAt)],
            ['Завершен', isoDateTime(run.finishedAt)],
            ['Длительность, мс', run.durationMs ?? ''],
            ['Ошибка', text(run.errorMessage, '-')],
            ['Snapshot IDs', formatList(run.snapshotIds)],
            ['Calculated modes', run.calculatedModesCount ?? 0],
            ['Booked modes', run.bookedModesCount ?? 0],
            ['Complan modes', run.complanModesCount ?? 0],
            ['Mixed modes', run.mixedModesCount ?? 0],
            ['Affected satellites', run.affectedSatellitesCount ?? 0],
            ['Processed cells', run.processedCellsCount ?? 0],
            ['Booked slot links', run.bookedSlotLinksCount ?? 0],
            ['Процент покрытия', Number(run.coveredAreaPercent) || 0]
        ];
    }

    function parseDate(value) {
        return PcpUtcTime.parseApiDate(value);
    }

    function intervalDurationDays(run) {
        const intervalStart = parseDate(run.intervalStart);
        const intervalEnd = parseDate(run.intervalEnd);
        if (!intervalStart || !intervalEnd || intervalEnd <= intervalStart) {
            return 0;
        }
        return (intervalEnd.getTime() - intervalStart.getTime()) / 86400000;
    }

    function modeDurationSeconds(mode) {
        const duration = Number(mode.duration);
        if (Number.isFinite(duration) && duration > 0) {
            return duration;
        }

        const startTime = parseDate(mode.startTime);
        const endTime = parseDate(mode.endTime);
        if (!startTime || !endTime || endTime <= startTime) {
            return 0;
        }
        return (endTime.getTime() - startTime.getTime()) / 1000;
    }

    async function loadSatelliteDetails(satelliteIds) {
        const uniqueIds = [...new Set(satelliteIds.filter(id => id !== null && id !== undefined && id !== ''))];
        const entries = await Promise.all(uniqueIds.map(async satelliteId => {
            try {
                const satellite = await requestJson(`/api/catalog/satellites/${encodeURIComponent(satelliteId)}`);
                return [String(satelliteId), satellite];
            } catch (error) {
                return [String(satelliteId), null];
            }
        }));
        return new Map(entries);
    }

    function satelliteLoadRows(run, modes, satellitesById) {
        const intervalDays = intervalDurationDays(run);
        const satelliteIds = [...new Set([
            ...(Array.isArray(run.satelliteIds) ? run.satelliteIds : []),
            ...modes.map(mode => mode.satelliteId)
        ].filter(id => id !== null && id !== undefined && id !== ''))]
            .sort((left, right) => Number(left) - Number(right));

        return satelliteIds.map(satelliteId => {
            const satellite = satellitesById.get(String(satelliteId));
            const dailyMaxDurationSeconds = Number(satellite?.observationProfile?.dailyMaxDurationSeconds);
            const totalSurveySeconds = modes
                .filter(mode => String(mode.satelliteId) === String(satelliteId))
                .filter(isSurveyMode)
                .reduce((sum, mode) => sum + modeDurationSeconds(mode), 0);
            const capacitySeconds = Number.isFinite(dailyMaxDurationSeconds) && dailyMaxDurationSeconds > 0
                ? intervalDays * dailyMaxDurationSeconds
                : 0;
            const loadFactor = capacitySeconds > 0
                ? (totalSurveySeconds / capacitySeconds) * 100
                : null;

            return [
                Number(satelliteId),
                satellite?.name ?? '',
                intervalDays,
                Number.isFinite(dailyMaxDurationSeconds) ? dailyMaxDurationSeconds / 86400 : '',
                Number.isFinite(dailyMaxDurationSeconds) ? dailyMaxDurationSeconds : '',
                capacitySeconds,
                totalSurveySeconds,
                loadFactor === null ? '' : loadFactor
            ];
        });
    }

    function modeRows(modes) {
        return modes
            .slice()
            .sort((left, right) => String(left.startTime || '').localeCompare(String(right.startTime || '')))
            .map(mode => [
                mode.id ?? '',
                mode.satelliteId ?? '',
                mode.source ?? '',
                mode.type ?? '',
                isoDateTime(mode.startTime),
                isoDateTime(mode.endTime),
                mode.revolution ?? '',
                mode.roll ?? '',
                mode.lat ?? '',
                mode.longitude ?? '',
                mode.duration ?? '',
                Array.isArray(mode.cellNums) ? mode.cellNums.join(', ') : text(mode.cellNum, ''),
                Array.isArray(mode.bookedSlotIds) ? mode.bookedSlotIds.join(', ') : '',
                mode.snapshotId ?? '',
                mode.contourWkt ?? ''
            ]);
    }

    function downloadCsv(filename, rows) {
        const csv = '\uFEFF' + rows.map(csvRow).join('\n');
        const url = URL.createObjectURL(new Blob([csv], { type: 'text/csv;charset=utf-8;' }));
        const link = document.createElement('a');
        link.href = url;
        link.download = filename;
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        URL.revokeObjectURL(url);
    }

    async function downloadReport() {
        const run = state.selectedRun;
        const runId = run?.runId ?? run?.id;
        if (!runId) {
            showAlert('Выберите расчет для формирования отчета');
            return;
        }

        const reportButton = document.getElementById('complex-plan-report');
        reportButton.disabled = true;
        try {
            const modes = await requestJson(`${apiBase}/${encodeURIComponent(runId)}/modes`);
            const normalizedModes = Array.isArray(modes) ? modes : [];
            const satelliteIds = [
                ...(Array.isArray(run.satelliteIds) ? run.satelliteIds : []),
                ...normalizedModes.map(mode => mode.satelliteId)
            ];
            const satellitesById = await loadSatelliteDetails(satelliteIds);
            const rows = [
                ['Детальная информация о плане'],
                ['Параметр', 'Значение'],
                ...runDetailRows(run),
                [],
                ['Добавлено съемок по спутникам'],
                ['satelliteId', 'addedSurveyModesCount'],
                ...addedSurveyRowsBySatellite(run, normalizedModes),
                [],
                ['Сводная таблица по загруженности спутников'],
                [
                    'satelliteId',
                    'satelliteName',
                    'calculationDurationDays',
                    'dailyMaxDurationDays',
                    'dailyMaxDurationSeconds',
                    'availableDurationSeconds',
                    'surveyModesDurationSeconds',
                    'loadFactor'
                ],
                ...satelliteLoadRows(run, normalizedModes, satellitesById),
                [],
                ['Режимы спутников'],
                [
                    'modeId',
                    'satelliteId',
                    'source',
                    'type',
                    'startTime',
                    'endTime',
                    'revolution',
                    'roll',
                    'lat',
                    'longitude',
                    'duration',
                    'cellNums',
                    'bookedSlotIds',
                    'snapshotId',
                    'contourWkt'
                ],
                ...modeRows(normalizedModes)
            ];
            downloadCsv(`complex-plan-${runId}.csv`, rows);
            showAlert(`Отчет по расчету #${runId} сформирован`, 'success');
        } catch (error) {
            showAlert(error.message || 'Не удалось сформировать отчет');
        } finally {
            reportButton.disabled = false;
        }
    }

    document.getElementById('complex-plan-refresh').addEventListener('click', () => loadRuns());
    document.getElementById('complex-plan-process-form').addEventListener('submit', submitProcessForm);
    document.getElementById('complex-plan-report').addEventListener('click', downloadReport);
    loadRuns();
})();
