function deliveryPostJson(endpoint, payload) {
    return fetch(ROOT_URL + endpoint, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify(payload)
    }).then(function(response) {
        if (!response.ok) {
            throw new Error(endpoint + ' failed');
        }
        return response.json();
    }).then(function(data) {
        if (!data || data.ok !== true) {
            throw new Error((data && data.error) ? data.error : endpoint + ' returned an error');
        }
        return data;
    });
}

function deliveryManifestChainTitle(chainId) {
    if (!chainId) {
        return '';
    }
    const chainCheck = Array.from(document.querySelectorAll('.delivery-chain-check')).find(function(check) {
        return check.value === chainId;
    });
    return chainCheck?.getAttribute('data-label') || chainId;
}

function deliveryInputIsSelected(input) {
    if (!input) {
        return false;
    }

    const holder = input.closest('.holder');
    const selectedTokens = window.deliverySelectedTokens instanceof Set ? window.deliverySelectedTokens : new Set();
    return !!input.checked ||
        input.dataset.selected === '1' ||
        holder?.dataset?.selected === '1' ||
        selectedTokens.has(input.name);
}

function deliveryBuildCoverReports(reports) {
    const coverReports = [];
    const coverReportTokens = new Set();

    (reports || []).forEach(function(report) {
        const token = deliveryReportTokenForLegacyUpdate(report);
        const expandedReports = window.deliveryPuwerCoverReportMap?.[token] || [report];
        expandedReports.forEach(function(coverReport) {
            const coverToken = deliveryReportTokenForLegacyUpdate(coverReport);
            if (!coverReportTokens.has(coverToken)) {
                coverReports.push(coverReport);
                coverReportTokens.add(coverToken);
            }
        });
    });

    return coverReports;
}

function deliveryBuildManifest(profile, orderedTokens) {
    const reports = [];
    const reportsByToken = new Map();
    const allReportsByToken = new Map();
    const selectedTokens = window.deliverySelectedTokens instanceof Set ? window.deliverySelectedTokens : new Set();

    document.querySelectorAll('.repitem').forEach(function(input) {
        if (!input.name) {
            return;
        }

        const parts = input.name.split('-');
        const holder = input.closest('.holder');
        const chainId = holder?.dataset?.chainId || '';
        const report = {
            repnumber: parseInt(parts[0] || '0', 10),
            itemnumber: parseInt(parts[1] || '0', 10),
            form: parseInt(parts[2] || '0', 10),
            surveyor: parseInt(parts[3] || '0', 10),
            status: input.getAttribute('data-status') || holder?.dataset?.status || '',
            chainId: chainId,
            chainTitle: deliveryManifestChainTitle(chainId),
            locationName: holder?.dataset?.locationName || ''
        };

        allReportsByToken.set(input.name, report);

        if (deliveryInputIsSelected(input)) {
            reports.push(report);
            reportsByToken.set(input.name, report);
        }
    });

    const sortedTokens = deliveryListFromAny(orderedTokens);
    const sortedReports = sortedTokens.length
        ? sortedTokens.map(function(token) {
            return reportsByToken.get(token) || (reports.length === 0 ? allReportsByToken.get(token) : null);
        }).filter(Boolean)
        : reports;

    if (!sortedReports.length && selectedTokens.size) {
        selectedTokens.forEach(function(token) {
            const report = allReportsByToken.get(token);
            if (report) {
                sortedReports.push(report);
            }
        });
    }

    return {
        clientnumber: <?= (int)$clientnumber ?>,
        site: parseInt(deliverySelectedSite || '0', 10),
        profile: profile || 'single',
        output: {
            mode: document.querySelector("input[name='outputMode']:checked")?.value || 'pdf',
            type: document.querySelector("input[name='outputType']:checked")?.value || 'rep',
            chainMode: document.querySelector("input[name='chainMode']:checked")?.value || 'merge'
        },
        options: {
            attachSchedule: !!document.getElementById('schAttach')?.checked,
            attachWrittenScheme: !!document.getElementById('wsAttach')?.checked,
            chainCovers: !!document.getElementById('splitCoverByChain')?.checked,
            coverTemplate: document.getElementById('coverTemplate')?.value || 'default',
            dateStart: document.getElementById('date_start2')?.value || document.getElementById('date_start')?.value || '',
            dateEnd: document.getElementById('date_end2')?.value || document.getElementById('date_end')?.value || '',
            forceGenerate: !!document.getElementById('forceGen')?.checked,
            bypassDeliveryUpdate: !!document.getElementById('bypass')?.checked
        },
        reports: sortedReports,
        coverReports: deliveryBuildCoverReports(sortedReports),
        files: []
    };
}

function deliveryReportTokenForLegacyUpdate(report) {
    return [
        report.repnumber || 0,
        report.itemnumber || 0,
        report.form || 0,
        report.surveyor || 0
    ].join('-');
}

function deliveryAddFiles(manifest, files) {
    const seen = new Set((manifest.files || []).map(function(file) {
        return (file.source || '') + '/' + (file.file || '');
    }));

    (files || []).forEach(function(file) {
        const key = (file.source || '') + '/' + (file.file || '');
        if (!seen.has(key)) {
            manifest.files.push(file);
            seen.add(key);
        }
    });
}

function deliveryAddWarnings(manifest, warnings) {
    const existing = Array.isArray(manifest.warnings) ? manifest.warnings : [];
    const merged = existing.concat(Array.isArray(warnings) ? warnings : []);
    manifest.warnings = Array.from(new Set(merged.filter(function(message) {
        return String(message || '').trim() !== '';
    })));
}

function deliverySetStatus(text, icon) {
    $('#statusCount').html(text + (icon ? '<br><i class="' + icon + '"></i>' : ''));
}

function deliveryGenerateScheduleManifest(manifest) {
    if (!manifest.options.attachSchedule) {
        return Promise.resolve(manifest);
    }

    deliverySetStatus('Generating Schedule', 'fad clipboard-list');
    return deliveryPostJson('/functions/delivery/generate_schedule.php', {
        manifest: manifest,
        tenant: TENANT_URL
    }).then(function(data) {
        deliveryAddFiles(manifest, data.files);
        return manifest;
    });
}

function deliveryGenerateCoverManifest(manifest) {
    deliverySetStatus('Generating Report Cover', 'fad fa-blanket');
    return deliveryPostJson('/functions/delivery/generate_covers.php', {
        manifest: manifest,
        tenant: TENANT_URL
    }).then(function(data) {
        deliveryAddFiles(manifest, data.files);
        return manifest;
    });
}

function deliveryGenerateSingleReport(manifest, report) {
    deliverySetStatus('Generating Report ' + report.repnumber, 'fad fa-file-pdf');
    return deliveryPostJson('/functions/delivery/generate_single.php', {
        manifest: manifest,
        report: report,
        tenant: TENANT_URL
    }).then(function(data) {
        deliveryAddFiles(manifest, data.files);
        return manifest;
    });
}

function deliveryGenerateSingles(manifest, reports) {
    return (reports || []).reduce(function(chain, report) {
        return chain.then(function() {
            return deliveryGenerateSingleReport(manifest, report);
        });
    }, Promise.resolve(manifest));
}

function deliveryMultiCapableFormsManifest() {
    return ['500', '502', '503', '504', '506', '900'];
}

function deliveryReportsByFormSurveyor(reports) {
    const grouped = {};
    reports.forEach(function(report) {
        const form = String(report.form || '');
        const surveyor = String(report.surveyor || '0');
        const key = form + '|' + surveyor;
        if (!grouped[key]) {
            grouped[key] = {
                form: form,
                surveyor: surveyor,
                reports: []
            };
        }
        grouped[key].reports.push(report);
    });
    return grouped;
}

function deliveryGenerateMultiGroup(manifest, group) {
    const form = group.form;
    const reports = group.reports || [];

    if (deliveryMultiCapableFormsManifest().indexOf(String(form)) === -1 || reports.length <= 1) {
        return deliveryGenerateSingles(manifest, reports);
    }

    deliverySetStatus('Generating Multi ' + form + ' Report', 'fad fa-file-spreadsheet');
    return deliveryPostJson('/functions/delivery/generate_multi.php', {
        manifest: manifest,
        reports: reports,
        form: parseInt(form, 10),
        tenant: TENANT_URL
    }).then(function(result) {
        deliveryAddFiles(manifest, result.files);
        return manifest;
    });
}

function deliveryGenerateReportsManifest(manifest) {
    if (manifest.output.type === 'cover') {
        return Promise.resolve(manifest);
    }

    if (manifest.profile !== 'multi') {
        return deliveryGenerateSingles(manifest, manifest.reports);
    }

    const grouped = deliveryReportsByFormSurveyor(manifest.reports);
    return Object.keys(grouped).reduce(function(chain, key) {
        return chain.then(function() {
            return deliveryGenerateMultiGroup(manifest, grouped[key]);
        });
    }, Promise.resolve(manifest));
}

function deliveryResolveAttachmentManifest(manifest) {
    return deliveryPostJson('/functions/delivery/resolve_attachments.php', {
        manifest: manifest,
        files: manifest.files,
        tenant: TENANT_URL
    }).then(function(data) {
        manifest.files = data.files || manifest.files;
        deliveryAddWarnings(manifest, data.warnings || []);
        return manifest;
    });
}

function deliveryPackageZipManifest(manifest) {
    deliverySetStatus('Generating Zip', 'fad fa-file-archive');
    return deliveryPostJson('/functions/delivery/package_zip.php', {
        manifest: manifest,
        files: manifest.files,
        tenant: TENANT_URL
    }).then(function(data) {
        deliveryAddWarnings(manifest, data.warnings || []);
        return data.files && data.files[0] ? data.files[0] : null;
    });
}

function deliveryMailerContext(manifest) {
    const siteSelect = document.getElementById('siteList2') || document.getElementById('siteList');
    const selectedSiteOption = siteSelect?.options?.[siteSelect.selectedIndex] || null;
    let locationName = selectedSiteOption && selectedSiteOption.value !== ''
        ? selectedSiteOption.textContent.replace(/^\s*\S+\s+-\s+/, '').trim()
        : '';

    if (locationName === '') {
        const selectedLocations = Array.from(new Set((manifest.reports || [])
            .map(function(report) {
                return String(report.locationName || '').trim();
            })
            .filter(Boolean)));

        if (selectedLocations.length === 1) {
            locationName = selectedLocations[0];
        } else if (selectedLocations.length > 1) {
            locationName = 'multiple locations';
        }
    }

    return {
        clientnumber: manifest.clientnumber,
        site: manifest.site || 0,
        locationName: locationName,
        dateStart: manifest.options?.dateStart || '',
        dateEnd: manifest.options?.dateEnd || ''
    };
}

function deliveryCompleteManifest(manifest) {
    if (manifest.output.mode === 'zip') {
        return deliveryPackageZipManifest(manifest).then(function(zipFile) {
            if (zipFile) {
                document.getElementById('delivery').style.display = 'block';
                document.getElementById('mailerContainer').innerHTML = '';
                deliveryLoadMailer(Object.assign(deliveryMailerContext(manifest), {
                    zips: zipFile,
                    clientnumber: manifest.clientnumber,
                    deliveryWarnings: manifest.warnings || []
                }));
            }
            return manifest;
        });
    }

    return deliveryResolveAttachmentManifest(manifest).then(function(finalManifest) {
        document.getElementById('delivery').style.display = 'block';
        document.getElementById('mailerContainer').innerHTML = '';
        deliveryLoadMailer(Object.assign(deliveryMailerContext(finalManifest), {
            attachments: finalManifest.files,
            clientnumber: finalManifest.clientnumber,
            deliveryWarnings: finalManifest.warnings || []
        }));
        return finalManifest;
    });
}

function deliverySelectedReportPayload(profile) {
    const manifest = deliveryBuildManifest(profile);
    const selectedTokens = manifest.reports.map(deliveryReportTokenForLegacyUpdate);
    const errorTokens = [];
    const hseFiles = [];

    manifest.reports.forEach(function(report) {
        if (String(report.status || '').toUpperCase() === 'URGENT') {
            const token = deliveryReportTokenForLegacyUpdate(report);
            errorTokens.push(token);
            hseFiles.push('../output/reports/' + manifest.clientnumber + '/' + report.repnumber + '_' + report.itemnumber + '.pdf');
        }
    });

    return {
        manifest: manifest,
        selected: selectedTokens,
        errordel: errorTokens,
        hse: hseFiles.join(',')
    };
}

function deliveryPromptSort(profile) {
    const payload = deliverySelectedReportPayload(profile);
    if (!payload.manifest.reports.length) {
        showResponse('warning', 'No Items Selected');
        return;
    }

    selected = deliveryListToJson(payload.selected);
    errordel = deliveryListToJson(payload.errordel);
    hse = payload.hse;
    deliveryStoreList('itemList', payload.selected);
    deliveryStoreList('multiList', profile === 'multi' ? payload.selected : []);

    const forceGen = payload.manifest.options.forceGenerate ? 'yes' : '';
    const chainMode = payload.manifest.output.chainMode || 'merge';

    if (profile === 'single' && payload.selected.length <= 1) {
        deliveryRunManifest('single', payload.selected);
        return;
    }

    if (profile === 'multi') {
        loadModalContent(
            '<p class="text-center mb-0">Do you wish to manually sort the reports before producing the multiform output?</p>',
            "multiBox",
            "modal-xl",
            1060,
            "true",
            'Manual Sort',
            'sortReports',
            'genPack(' + deliveryJsArg(selected) + ',' + deliveryJsArg(errordel) + ',' + deliveryJsArg(hse) + ')',
            'manualSort(' + deliveryJsArg(selected) + ',' + deliveryJsArg(errordel) + ',' + deliveryJsArg(hse) + ')'
        );
        return;
    }

    loadModalContent(
        '<h3 class="text-center mb-0">Do you wish to manually sort the reports?</h3>',
        "multiBox",
        "modal-xl",
        1060,
        "true",
        'Manual Sort',
        'sortReports',
        'singleContinue(' + deliveryJsArg(selected) + ',' + deliveryJsArg(errordel) + ',' + deliveryJsArg(forceGen) + ',' + deliveryJsArg(hse) + ',' + deliveryJsArg(chainMode) + ')',
        'manualSortSingle(' + deliveryJsArg(selected) + ',' + deliveryJsArg(errordel) + ',' + deliveryJsArg(forceGen) + ',' + deliveryJsArg(hse) + ',' + deliveryJsArg(chainMode) + ')'
    );
}

function deliveryRunManifest(profile, orderedTokens) {
    const requestedTokens = deliveryListFromAny(orderedTokens);
    if (!requestedTokens.length) {
        const storedList = document.getElementById(profile === 'multi' ? 'multiList' : 'itemList');
        const storedTokens = deliveryListFromAny(storedList?.textContent || storedList?.value || '');
        if (storedTokens.length) {
            orderedTokens = storedTokens;
        }
    }

    const manifest = deliveryBuildManifest(profile, orderedTokens);
    if (!manifest.reports.length) {
        showResponse('warning', 'No Items Selected');
        return;
    }

    $('#deliveryScreen').css('display', 'none');
    $('#loading').css('display', 'block');
    deliveryStoreList('itemList', manifest.reports.map(deliveryReportTokenForLegacyUpdate));
    deliveryStoreList('multiList', manifest.profile === 'multi' ? manifest.reports.map(deliveryReportTokenForLegacyUpdate) : []);

    deliveryGenerateScheduleManifest(manifest)
        .then(deliveryGenerateCoverManifest)
        .then(deliveryGenerateReportsManifest)
        .then(deliveryCompleteManifest)
        .then(function() {
            $('#loading').css('display', 'none');
        })
        .catch(function(error) {
            console.error(error);
            $('#loading').css('display', 'none');
            showResponse('danger', 'Unable to complete delivery output');
        });
}

function getChecks() {
    deliveryPromptSort('single');
}

function getChecksMulti() {
    deliveryPromptSort('multi');
}

function triggerZip() {
    deliveryPromptSort('multi');
}

function singleContinue(selected, errordel, forceGen, hse, chainMode) {
    killModal('multiBox');
    killModal('sortBox');
    deliveryRunManifest('single', selected);
}

function genPack(selected, errordel, hse) {
    killModal('multiBox');
    killModal('sortBox');
    deliveryRunManifest('multi', selected);
}
