<?php
if (file_exists('../../../' . $_REQUEST['tenant'] . '/config.php')) {
    require_once('../../../' . $_REQUEST['tenant'] . '/config.php');
}

$appointmentDate = isset($_REQUEST['appointment_date']) ? (int)$_REQUEST['appointment_date'] : strtotime('today midnight');
$startTime = isset($_REQUEST['start_time']) ? (int)$_REQUEST['start_time'] : 0;
$dateValue = date('d-m-Y', $appointmentDate);
$startValue = $startTime > 0 ? date('H:i', $startTime) : '';
$endValue = $startTime > 0 ? date('H:i', $startTime + 3600) : '';
?>
<style>
.admin-assignment-modal .alert {
    padding: .5rem .75rem;
}
.admin-assignment-modal .assignment-alert-stack {
    display: grid;
    gap: .5rem;
}
</style>
<div class="container-fluid admin-assignment-modal">
    <div class="row gy-3">
        <div class="col-12 col-lg-4">
            <label for="modalClientSelect" class="form-label">Client</label>
            <select id="modalClientSelect" class="form-select" data-mdb-select-init data-mdb-filter="true" data-mdb-container="#popupBox">
                <option value="">Select Client</option>
            </select>
        </div>
        <div class="col-12 col-lg-4">
            <label for="modalLocationSelect" class="form-label">Site</label>
            <select id="modalLocationSelect" class="form-select" data-mdb-select-init data-mdb-filter="true" data-mdb-container="#popupBox">
                <option value="">Choose location</option>
            </select>
        </div>
        <div class="col-12 col-lg-4">
            <label for="modalSurveyorSelect" class="form-label">Surveyor</label>
            <select id="modalSurveyorSelect" class="form-select">
                <option value="">Select Surveyor</option>
            </select>
        </div>
        <div class="col-12 col-md-4">
            <div class="datepicker" data-mdb-datepicker-init data-mdb-format="dd-mm-yyyy" data-mdb-input-init
                data-mdb-toggle-button="false" data-mdb-confirm-date-on-select="true">
                <label for="modalAppointmentDate" class="form-label">Date</label>
                <input type="text" id="modalAppointmentDate" class="form-control" data-mdb-toggle="datepicker"
                    value="<?= htmlspecialchars($dateValue) ?>">

            </div>
        </div>
        <div class="col-6 col-md-4">
            <div class="timepicker" data-mdb-timepicker-init data-mdb-with-icon="false" data-mdb-input-init>
                <label for="modalStartTime" class="form-label">Start</label>
                <input type="text" id="modalStartTime" class="form-control" data-mdb-toggle="timepicker"
                    value="<?= htmlspecialchars($startValue) ?>">

            </div>
        </div>
        <div class="col-6 col-md-4">
            <div class="timepicker" data-mdb-timepicker-init data-mdb-with-icon="false" data-mdb-input-init>
                <label for="modalEndTime" class="form-label">End</label>
                <input type="text" id="modalEndTime" class="form-control" data-mdb-toggle="timepicker"
                    value="<?= htmlspecialchars($endValue) ?>">

            </div>
        </div>
        <div class="col-12">
            <label for="modalAssignmentNotes" class="form-label">Notes</label>
            <textarea id="modalAssignmentNotes" class="form-control" rows="4"></textarea>
        </div>
        <div class="col-12 text-end">
            <button class="btn btn-outline-secondary" type="button" onclick="killModal('popupBox')">Cancel</button>
            <button class="btn btn-success" type="button" onclick="modalAssignSurveyor()">Assign Surveyor</button>
        </div>
        <div class="col-12">
            <div class="assignment-alert-stack">
                <div id="modalUiEstimate" class="alert alert-info mb-0" style="display:none"></div>
                <div id="modalUiWarning" class="alert alert-danger mb-0" style="display:none"></div>
                <div id="modalTravelEstimate" class="alert alert-light border mb-0" style="display:none"></div>
                <div id="modalTravelSuccess" class="alert alert-success mb-0" style="display:none"></div>
                <div id="modalInitialTravelWarning" class="alert alert-warning mb-0" style="display:none"></div>
                <div id="modalTravelWarning" class="alert alert-danger mb-0" style="display:none"></div>
            </div>
        </div>
    </div>
</div>

<script>
function modalEscapeHtml(value) {
    return String(value || '').replace(/[&<>"']/g, function(chr) {
        return ({
            '&': '&amp;',
            '<': '&lt;',
            '>': '&gt;',
            '"': '&quot;',
            "'": '&#039;'
        } [chr]);
    });
}

function modalSelectedText(id) {
    const select = document.getElementById(id);
    return select.options[select.selectedIndex] ? select.options[select.selectedIndex].text : '';
}

function modalDateToTimestamp(value) {
    if (!value) return '';
    let date;
    if (/^\d{4}-\d{2}-\d{2}$/.test(value)) {
        date = new Date(value + 'T00:00:00');
    } else {
        const parts = value.split('-');
        if (parts.length !== 3) return '';
        date = new Date(parseInt(parts[2], 10), parseInt(parts[1], 10) - 1, parseInt(parts[0], 10), 0, 0, 0);
    }
    if (isNaN(date.getTime())) return '';
    return Math.floor(date.getTime() / 1000);
}

function modalTimeToTimestamp(dateValue, timeValue) {
    if (!dateValue || !timeValue) return '';
    const dayTimestamp = modalDateToTimestamp(dateValue);
    if (!dayTimestamp) return '';
    const time = modalNormaliseTime(timeValue);
    if (!time) return '';
    const date = new Date(dayTimestamp * 1000);
    date.setHours(time.hours, time.minutes, 0, 0);
    return Math.floor(date.getTime() / 1000);
}

function modalDbTimeToTimestamp(dateValue, timeValue) {
    if (!timeValue) return '';
    return modalTimeToTimestamp(dateValue, String(timeValue).slice(0, 5));
}

function modalNormaliseTime(value) {
    value = String(value || '').trim().toLowerCase();
    const match = value.match(/^(\d{1,2})(?::(\d{2}))?\s*(am|pm)?$/);
    if (!match) return null;
    let hours = parseInt(match[1], 10);
    const minutes = match[2] ? parseInt(match[2], 10) : 0;
    const meridiem = match[3] || '';
    if (meridiem === 'pm' && hours < 12) hours += 12;
    if (meridiem === 'am' && hours === 12) hours = 0;
    if (hours < 0 || hours > 23 || minutes < 0 || minutes > 59) return null;
    return {
        hours,
        minutes
    };
}

function modalInitMdbPickers() {
    if (typeof mdb === 'undefined') return;
    document.querySelectorAll('#popupBox [data-mdb-input-init]').forEach(function(el) {
        if (typeof mdb.Input !== 'undefined') {
            try {
                new mdb.Input(el).init();
            } catch (e) {}
        }
    });
    document.querySelectorAll('#popupBox [data-mdb-datepicker-init]').forEach(function(el) {
        if (typeof mdb.Datepicker !== 'undefined' && !el.dataset.mdbDatepickerReady) {
            try {
                new mdb.Datepicker(el);
                el.dataset.mdbDatepickerReady = '1';
            } catch (e) {}
        }
    });
    document.querySelectorAll('#popupBox [data-mdb-timepicker-init]').forEach(function(el) {
        if (typeof mdb.Timepicker !== 'undefined' && !el.dataset.mdbTimepickerReady) {
            try {
                new mdb.Timepicker(el);
                el.dataset.mdbTimepickerReady = '1';
            } catch (e) {}
        }
    });
    document.querySelectorAll('#popupBox [data-mdb-select-init]').forEach(function(el) {
        modalInitMdbSelect(el);
    });
}

function modalInitMdbSelect(select) {
    if (typeof mdb === 'undefined' || typeof mdb.Select === 'undefined' || !select) return;
    try {
        const instance = mdb.Select.getInstance(select);
        if (instance) instance.dispose();
    } catch (e) {}
    try {
        new mdb.Select(select);
    } catch (e) {}
}

function modalLoadAssignmentOptions() {
    fetch(ROOT_URL + '/includes/admin/admin_get_clients.php?tenant=' + TENANT_URL)
        .then(response => response.json())
        .then(data => {
            const clientSelect = document.getElementById('modalClientSelect');
            clientSelect.innerHTML = '<option value="">Select Client</option>';
            (data.clients || []).forEach(client => {
                clientSelect.innerHTML +=
                    `<option value="${client.clientnumber}" data-map-query="${modalEscapeHtml(client.map_query || '')}">${modalEscapeHtml(client.clientname)} - ${client.expected_inspections} Expected Inspections</option>`;
            });
            modalInitMdbSelect(clientSelect);
        });

    fetch(ROOT_URL + '/includes/admin/admin_get_surveyors.php?tenant=' + TENANT_URL)
        .then(response => response.json())
        .then(data => {
            const surveyorSelect = document.getElementById('modalSurveyorSelect');
            surveyorSelect.innerHTML = '<option value="">Select Surveyor</option>';
            (data.surveyors || []).forEach(surveyor => {
                surveyorSelect.innerHTML +=
                    `<option value="${surveyor.id}" data-home-query="${modalEscapeHtml(surveyor.home_map_query || '')}" data-day-start-time="${modalEscapeHtml(surveyor.day_start_time || '')}">${modalEscapeHtml(surveyor.forename + ' ' + surveyor.surname)}</option>`;
            });
        });
}

function modalLoadLocationOptions() {
    const clientId = document.getElementById('modalClientSelect').value;
    const locationSelect = document.getElementById('modalLocationSelect');
    locationSelect.innerHTML = '<option value="">Choose location</option>';
    modalInitMdbSelect(locationSelect);
    modalCheckAssignmentUiEstimate();
    modalClearTravelChecks();
    if (!clientId) return;
    fetch(ROOT_URL + '/includes/admin/admin_get_locations.php?tenant=' + TENANT_URL + '&client_id=' +
            encodeURIComponent(clientId))
        .then(response => response.json())
        .then(data => {
            locationSelect.innerHTML = '<option value="">Choose location</option><option value="0">Whole client / no site</option>';
            (data.locations || []).forEach(location => {
                const label = `${location.locname}${location.postcode ? ' - ' + location.postcode : ''}`;
                locationSelect.innerHTML +=
                    `<option value="${location.locnumber}" data-map-query="${modalEscapeHtml(location.address || '')}">${modalEscapeHtml(label)}</option>`;
            });
            modalInitMdbSelect(locationSelect);
            modalTriggerAssignmentChecks();
        });
}

function modalSelectedMapQuery(selectId) {
    const select = document.getElementById(selectId);
    if (!select || !select.options[select.selectedIndex]) return '';
    return select.options[select.selectedIndex].dataset.mapQuery || '';
}

function modalAssignmentDestination() {
    return modalSelectedMapQuery('modalLocationSelect') || modalSelectedMapQuery('modalClientSelect');
}

function modalSelectedSurveyorTravelData() {
    const select = document.getElementById('modalSurveyorSelect');
    if (!select || !select.options[select.selectedIndex]) return { homeQuery: '', dayStartTime: '' };
    const option = select.options[select.selectedIndex];
    return {
        homeQuery: option.dataset.homeQuery || '',
        dayStartTime: option.dataset.dayStartTime || ''
    };
}

function modalSetTravelEstimate(message) {
    const target = document.getElementById('modalTravelEstimate');
    if (!target) return;
    target.style.display = message ? '' : 'none';
    target.innerHTML = message || '';
}

function modalSetTravelWarning(message) {
    const target = document.getElementById('modalTravelWarning');
    if (!target) return;
    target.style.display = message ? '' : 'none';
    target.innerHTML = message || '';
}

function modalSetInitialTravelWarning(message) {
    const target = document.getElementById('modalInitialTravelWarning');
    if (!target) return;
    target.style.display = message ? '' : 'none';
    target.innerHTML = message || '';
}

function modalSetTravelSuccess(message) {
    const target = document.getElementById('modalTravelSuccess');
    if (!target) return;
    target.style.display = message ? '' : 'none';
    target.innerHTML = message || '';
}

function modalClearTravelChecks() {
    modalSetTravelEstimate('');
    modalSetTravelSuccess('');
    modalSetInitialTravelWarning('');
    modalSetTravelWarning('');
}

function modalTriggerAssignmentChecks() {
    modalCheckAssignmentUiEstimate();
    const surveyorSelect = document.getElementById('modalSurveyorSelect');
    if (surveyorSelect && surveyorSelect.value) {
        modalCheckSurveyorTravel();
    } else {
        modalClearTravelChecks();
    }
}

function modalSetUiEstimate(message, units, missing) {
    const target = document.getElementById('modalUiEstimate');
    if (!target) return;
    target.dataset.uiUnits = units || 0;
    target.dataset.uiMissing = missing || 0;
    target.style.display = message ? '' : 'none';
    target.innerHTML = message || '';
}

function modalSetUiWarning(message) {
    const target = document.getElementById('modalUiWarning');
    if (!target) return;
    target.style.display = message ? '' : 'none';
    target.innerHTML = message || '';
}

function modalFormatUiUnits(units) {
    return Number(units || 0).toFixed(2).replace(/\.?0+$/, '');
}

function modalFormatMinutes(minutes) {
    minutes = Math.round(Number(minutes || 0));
    if (minutes < 60) return minutes + ' mins';
    const hours = Math.floor(minutes / 60);
    const rem = minutes % 60;
    return hours + 'h' + (rem ? ' ' + rem + 'm' : '');
}

function modalUpdateUiDurationWarning() {
    const estimate = document.getElementById('modalUiEstimate');
    if (!estimate) return;
    const units = Number(estimate.dataset.uiUnits || 0);
    const expectedMinutes = units * 15;
    const dateValue = document.getElementById('modalAppointmentDate').value;
    const start = modalTimeToTimestamp(dateValue, document.getElementById('modalStartTime').value);
    const end = modalTimeToTimestamp(dateValue, document.getElementById('modalEndTime').value);
    modalSetUiWarning('');
    if (!expectedMinutes || !start || !end) return;
    const scheduledMinutes = Math.max(0, (Number(end) - Number(start)) / 60);
    if (scheduledMinutes < expectedMinutes) {
        modalSetUiWarning(`Scheduled time is ${modalFormatMinutes(scheduledMinutes)}, but the UI estimate is ${modalFormatMinutes(expectedMinutes)}. Short by ${modalFormatMinutes(expectedMinutes - scheduledMinutes)}.`);
    }
}

var modalUiCheckToken = 0;

function modalCheckAssignmentUiEstimate() {
    const token = ++modalUiCheckToken;
    modalSetUiEstimate('', 0, 0);
    modalSetUiWarning('');
    const clientId = document.getElementById('modalClientSelect').value;
    const locationId = document.getElementById('modalLocationSelect').value;
    if (!clientId || locationId === '') return;

    modalSetUiEstimate('Calculating estimated inspection time...', 0, 0);
    fetch(ROOT_URL + '/includes/admin/admin_get_assignment_ui_estimate.php?tenant=' + TENANT_URL + '&client_id=' + encodeURIComponent(clientId) + '&location_id=' + encodeURIComponent(locationId))
        .then(response => response.ok ? response.json() : Promise.reject())
        .then(data => {
            if (token !== modalUiCheckToken) return;
            const units = Number(data.ui_units || 0);
            const missing = Number(data.missing_ui || 0);
            const items = Number(data.due_overdue_items || 0);
            const parts = [`Estimated inspection time: <strong>${modalFormatUiUnits(units)} UI</strong> (${modalFormatMinutes(units * 15)}) from ${items} due/overdue item${items === 1 ? '' : 's'}.`];
            if (missing > 0) parts.push(`${missing} item${missing === 1 ? '' : 's'} do not have a UI value.`);
            modalSetUiEstimate(parts.join(' '), units, missing);
            modalUpdateUiDurationWarning();
        })
        .catch(() => {
            if (token !== modalUiCheckToken) return;
            modalSetUiEstimate('Estimated inspection time is unavailable for this site.', 0, 0);
        });
}

function modalFormatClock(timestamp) {
    if (!timestamp) return '';
    const date = new Date(Number(timestamp) * 1000);
    return date.toTimeString().slice(0, 5);
}

function modalFormatTravelDistance(metres) {
    if (typeof formatDistance === 'function') return formatDistance(metres);
    const miles = metres / 1609.344;
    return miles >= 10 ? miles.toFixed(0) + ' miles' : miles.toFixed(1) + ' miles';
}

function modalFormatTravelDuration(seconds) {
    if (typeof formatDuration === 'function') return formatDuration(seconds);
    const mins = Math.round(seconds / 60);
    if (mins < 60) return mins + ' mins';
    const hours = Math.floor(mins / 60);
    const rem = mins % 60;
    return hours + 'h' + (rem ? ' ' + rem + 'm' : '');
}

function modalParseLatLng(value) {
    const match = String(value || '').trim().match(/^(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)$/);
    if (!match) return null;
    return { lat: parseFloat(match[1]), lon: parseFloat(match[2]) };
}

function modalExtractUkPostcode(value) {
    const matches = String(value || '').toUpperCase().match(/[A-Z]{1,2}\d[A-Z\d]?\s*\d[A-Z]{2}/g) || [];
    for (const candidate of matches) {
        const postcode = modalNormaliseUkPostcode(candidate);
        if (postcode) return postcode;
    }
    const compactMatches = String(value || '').toUpperCase().replace(/[^A-Z0-9]/g, ' ').split(/\s+/);
    for (const candidate of compactMatches) {
        const postcode = modalNormaliseUkPostcode(candidate);
        if (postcode) return postcode;
    }
    return '';
}

function modalNormaliseUkPostcode(postcode) {
    const clean = String(postcode || '').toUpperCase().replace(/[^A-Z0-9]/g, '');
    if (!/^[A-Z]{1,2}\d[A-Z\d]?\d[A-Z]{2}$/.test(clean)) return '';
    return clean.slice(0, -3) + ' ' + clean.slice(-3);
}

function modalFetchJsonWithTimeout(url, timeoutMs = 8000) {
    const controller = new AbortController();
    const timeout = setTimeout(() => controller.abort(), timeoutMs);
    return fetch(url, { signal: controller.signal })
        .then(response => {
            clearTimeout(timeout);
            if (!response.ok) {
                const error = new Error('Request failed with status ' + response.status);
                error.status = response.status;
                throw error;
            }
            return response.json();
        })
        .catch(error => {
            clearTimeout(timeout);
            throw error;
        });
}

function modalGeocodePostcode(postcode) {
    return modalFetchJsonWithTimeout('https://api.postcodes.io/postcodes/' + encodeURIComponent(postcode), 5000)
        .then(data => {
            if (data && data.result) return { lat: data.result.latitude, lon: data.result.longitude };
            return Promise.reject();
        });
}

function modalGeocodeWithNominatim(value) {
    return modalFetchJsonWithTimeout('https://nominatim.openstreetmap.org/search?format=json&limit=1&q=' + encodeURIComponent(value), 8000)
        .then(data => {
            if (data && data[0]) return { lat: parseFloat(data[0].lat), lon: parseFloat(data[0].lon) };
            return Promise.reject();
        });
}

function modalGeocodeMapQuery(value) {
    if (typeof geocodeMapQuery === 'function') return geocodeMapQuery(value);
    const query = String(value || '').trim();
    const coords = modalParseLatLng(value);
    if (coords) return Promise.resolve(coords);
    const postcode = modalExtractUkPostcode(query);
    if (postcode) {
        return modalGeocodePostcode(postcode).catch(() => modalGeocodeAddressFallbacks(query, postcode));
    }
    return modalGeocodeAddressFallbacks(query, '').catch(() => Promise.reject(new Error('Unable to geocode ' + query)));
}

function modalGeocodeAddressFallbacks(query, postcode) {
    const parts = String(query || '').split(',').map(part => part.trim()).filter(Boolean);
    const attempts = [query];
    if (parts.length > 3) attempts.push(parts.slice(-3).join(', '));
    if (parts.length > 2) attempts.push(parts.slice(-2).join(', '));
    if (postcode) attempts.push(postcode);
    const uniqueAttempts = attempts.filter((attempt, index) => attempt && attempts.indexOf(attempt) === index);

    return uniqueAttempts.reduce((chain, attempt) => {
        return chain.catch(() => modalGeocodeWithNominatim(attempt));
    }, Promise.reject());
}

function modalRouteEstimate(fromQuery, toQuery) {
    return modalGeocodeMapQuery(fromQuery).catch(error => {
        throw new Error('Could not find the origin address: ' + fromQuery);
    }).then(from => modalGeocodeMapQuery(toQuery).catch(error => {
        throw new Error('Could not find the destination address: ' + toQuery);
    }).then(to => ({ from, to }))).then(points => {
        const from = points.from;
        const to = points.to;
        const url = 'https://router.project-osrm.org/route/v1/driving/' + from.lon + ',' + from.lat + ';' + to.lon + ',' + to.lat + '?overview=false';
        return modalFetchJsonWithTimeout(url, 10000)
            .then(data => {
                if (!data.routes || !data.routes[0]) return Promise.reject(new Error('A route could not be calculated between these two locations.'));
                return data.routes[0];
            });
    });
}

function modalRouteMapButton(fromQuery, toQuery) {
    return ` <button type="button" class="btn btn-sm btn-light ms-2" onclick="modalOpenRouteMap('${encodeURIComponent(fromQuery)}', '${encodeURIComponent(toQuery)}')"><i class="fad fa-route me-1"></i>Show map</button>`;
}

function modalOpenRouteMap(encodedOrigin, encodedDestination) {
    const origin = decodeURIComponent(encodedOrigin || '');
    const destination = decodeURIComponent(encodedDestination || '');
    if (!origin || !destination) return;
    document.getElementById('mapOrigin').value = origin;
    document.getElementById('mapDestination').value = destination;
    renderJobMapRoute();
    if (typeof showAssignmentMapModal === 'function') {
        showAssignmentMapModal();
    } else {
        new mdb.Modal(document.getElementById('assignmentMapModal')).show();
    }
}

function modalSameDaySurveyorJobs(surveyorId, dayTimestamp) {
    const jobs = (typeof allJobs !== 'undefined' && Array.isArray(allJobs)) ? allJobs : [];
    const dayEnd = Number(dayTimestamp) + 86400;
    return jobs
        .filter(job => String(job.surveyor_id) === String(surveyorId))
        .filter(job => {
            const start = Number(job.start_time || job.appointment_date || 0);
            return start >= Number(dayTimestamp) && start < dayEnd;
        })
        .filter(job => job.map_query)
        .sort((a, b) => Number(a.start_time || a.appointment_date || 0) - Number(b.start_time || b.appointment_date || 0));
}

var modalTravelCheckToken = 0;

function modalCheckSurveyorTravel() {
    const token = ++modalTravelCheckToken;
    modalSetTravelEstimate('');
    modalSetTravelSuccess('');
    modalSetInitialTravelWarning('');
    modalSetTravelWarning('');

    const clientId = document.getElementById('modalClientSelect').value;
    const locationId = document.getElementById('modalLocationSelect').value;
    const surveyorId = document.getElementById('modalSurveyorSelect').value;
    const dateValue = document.getElementById('modalAppointmentDate').value;
    const appointmentDate = modalDateToTimestamp(dateValue);
    const destination = modalAssignmentDestination();

    if (!clientId || locationId === '' || !surveyorId || !appointmentDate || !destination) return;

    const startTime = modalTimeToTimestamp(dateValue, document.getElementById('modalStartTime').value);
    const endTime = modalTimeToTimestamp(dateValue, document.getElementById('modalEndTime').value);
    if (!startTime || !endTime) {
        modalSetTravelEstimate('Add a start and end time to check travel gaps against the surveyor diary.');
        return;
    }

    const sameDayJobs = modalSameDaySurveyorJobs(surveyorId, appointmentDate);
    const previous = sameDayJobs.filter(job => Number(job.start_time || job.appointment_date || 0) < Number(startTime)).pop();
    const next = sameDayJobs.find(job => Number(job.start_time || job.appointment_date || 0) > Number(startTime));
    const surveyorTravel = modalSelectedSurveyorTravelData();
    const homeStartTime = modalDbTimeToTimestamp(dateValue, surveyorTravel.dayStartTime);
    const checks = [];
    if (previous) checks.push({ type: 'previous', job: previous, from: previous.map_query, to: destination });
    if (!previous && surveyorTravel.homeQuery && homeStartTime) checks.push({ type: 'home', from: surveyorTravel.homeQuery, to: destination, homeStartTime });
    if (next) checks.push({ type: 'next', job: next, from: destination, to: next.map_query });

    if (!checks.length) {
        modalSetTravelEstimate('No same-day appointments found immediately before or after this slot for the selected surveyor. Add a surveyor home address and day start time to check first-job travel.');
        return;
    }

    modalSetTravelEstimate('Checking travel time against the selected surveyor diary... <i class="fa-duotone fa-regular fa-circle-notch fa-spin" style="--fa-primary-color: rgb(116, 153, 219); --fa-secondary-color: rgb(255, 255, 255); --fa-secondary-opacity: 0.27;"></i>');
    Promise.all(checks.map(check => modalRouteEstimate(check.from, check.to).then(route => ({ ...check, route }))))
        .then(results => {
            if (token !== modalTravelCheckToken) return;
            const lines = [];
            const warnings = [];
            const initialWarnings = [];
            const successLines = [];
            results.forEach(result => {
                const jobStart = result.job ? Number(result.job.start_time || result.job.appointment_date || 0) : 0;
                const jobEnd = result.job ? Number(result.job.end_time || jobStart || 0) : 0;
                const travelSeconds = Number(result.route.duration || 0);
                const distance = modalFormatTravelDistance(result.route.distance || 0);
                const duration = modalFormatTravelDuration(travelSeconds);
                if (result.type === 'previous') {
                    const available = Math.max(0, Number(startTime) - jobEnd);
                    const slack = Math.max(0, available - travelSeconds);
                    lines.push(`Previous: ${modalEscapeHtml(result.job.clientname || 'Appointment')} ${modalFormatClock(jobEnd)} to ${modalFormatClock(startTime)} - available gap ${modalFormatTravelDuration(available)}, ${distance}, approx ${duration}.`);
                    if (travelSeconds > available) {
                        warnings.push(`Previous appointment leaves ${modalFormatTravelDuration(available)}, but expected travel is ${duration}. Short by ${modalFormatTravelDuration(travelSeconds - available)}.${modalRouteMapButton(result.from, result.to)}`);
                    } else {
                        successLines.push(`Previous appointment leaves ${modalFormatTravelDuration(available)}; expected travel is ${duration}, leaving ${modalFormatTravelDuration(slack)} spare.`);
                    }
                } else if (result.type === 'home') {
                    const available = Math.max(0, Number(startTime) - Number(result.homeStartTime));
                    const slack = Math.max(0, available - travelSeconds);
                    lines.push(`Home start: ${modalFormatClock(result.homeStartTime)} to ${modalFormatClock(startTime)} - available gap ${modalFormatTravelDuration(available)}, ${distance}, approx ${duration}.`);
                    if (travelSeconds > available) {
                        initialWarnings.push(`First appointment leaves ${modalFormatTravelDuration(available)} from the surveyor day start, but expected travel from home is ${duration}. Short by ${modalFormatTravelDuration(travelSeconds - available)}.${modalRouteMapButton(result.from, result.to)}`);
                    } else {
                        successLines.push(`First appointment leaves ${modalFormatTravelDuration(available)} from home; expected travel is ${duration}, leaving ${modalFormatTravelDuration(slack)} spare.`);
                    }
                } else {
                    const available = Math.max(0, jobStart - Number(endTime));
                    const slack = Math.max(0, available - travelSeconds);
                    lines.push(`Next: ${modalEscapeHtml(result.job.clientname || 'Appointment')} ${modalFormatClock(endTime)} to ${modalFormatClock(jobStart)} - available gap ${modalFormatTravelDuration(available)}, ${distance}, approx ${duration}.`);
                    if (travelSeconds > available) {
                        warnings.push(`Next appointment leaves ${modalFormatTravelDuration(available)}, but expected travel is ${duration}. Short by ${modalFormatTravelDuration(travelSeconds - available)}.${modalRouteMapButton(result.from, result.to)}`);
                    } else {
                        successLines.push(`Next appointment leaves ${modalFormatTravelDuration(available)}; expected travel is ${duration}, leaving ${modalFormatTravelDuration(slack)} spare.`);
                    }
                }
            });
            modalSetTravelEstimate(lines.join('<br>'));
            modalSetInitialTravelWarning(initialWarnings.join('<br>'));
            if (warnings.length) {
                modalSetTravelWarning(warnings.join('<br>'));
                modalSetTravelSuccess(successLines.join('<br>'));
            } else {
                modalSetTravelWarning('');
                modalSetTravelSuccess(successLines.join('<br>'));
            }
        })
        .catch(error => {
            if (token !== modalTravelCheckToken) return;
            modalSetTravelSuccess('');
            modalSetInitialTravelWarning('');
            modalSetTravelEstimate(modalEscapeHtml(error && error.message ? error.message : 'Travel estimate is unavailable. Check the address and try again.'));
        });
}

function modalAssignSurveyor() {
    const dateValue = document.getElementById('modalAppointmentDate').value;
    const clientId = document.getElementById('modalClientSelect').value;
    const locationId = document.getElementById('modalLocationSelect').value;
    const surveyorId = document.getElementById('modalSurveyorSelect').value;
    const appointmentDate = modalDateToTimestamp(dateValue);
    if (!clientId || locationId === '' || !surveyorId || !appointmentDate) {
        showResponse('warning', 'Please select a client, site, surveyor and date.');
        return;
    }

    const body = new URLSearchParams({
        tenant: TENANT_URL,
        client_id: clientId,
        surveyor_id: surveyorId,
        appointment_date: appointmentDate,
        location_id: locationId,
        start_time: modalTimeToTimestamp(dateValue, document.getElementById('modalStartTime').value),
        end_time: modalTimeToTimestamp(dateValue, document.getElementById('modalEndTime').value),
        notes: document.getElementById('modalAssignmentNotes').value
    });
    const clientName = modalSelectedText('modalClientSelect').split(' - ')[0];

    fetch(ROOT_URL + '/includes/admin/admin_assign_surveyor.php', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded'
            },
            body: body.toString()
        })
        .then(response => response.text().then(text => {
            if (!response.ok) throw new Error(text || response.statusText);
            return text;
        }))
        .then(data => {
            if (data.includes('success')) {
                showResponse('success', data.replace('success:', '').trim() || 'Surveyor assigned successfully.');
                if (typeof sendAssignmentNotification === 'function') sendAssignmentNotification(surveyorId,
                    clientName);
                if (typeof loadAssignedJobs === 'function') loadAssignedJobs();
                killModal('popupBox');
            } else if (data.includes('already exists') || data.includes('overlaps')) {
                showResponse('warning', data.replace('error:', '').trim());
            } else {
                showResponse('danger', data.replace('error:', '').trim() || data || 'Error assigning surveyor.');
            }
        })
        .catch(error => showResponse('danger', error.message || 'Error assigning surveyor.'));
}

modalLoadAssignmentOptions();
setTimeout(modalInitMdbPickers, 0);
setTimeout(function() {
    ['modalAppointmentDate', 'modalStartTime', 'modalEndTime'].forEach(function(id) {
        const element = document.getElementById(id);
        if (element) {
            element.addEventListener('input', function() {
                modalTriggerAssignmentChecks();
            });
            element.addEventListener('change', function() {
                modalTriggerAssignmentChecks();
            });
        }
    });
    const clientSelect = document.getElementById('modalClientSelect');
    if (clientSelect) {
        clientSelect.addEventListener('change', modalLoadLocationOptions);
        clientSelect.addEventListener('valueChange.mdb.select', modalLoadLocationOptions);
    }
    ['modalLocationSelect', 'modalSurveyorSelect'].forEach(function(id) {
        const element = document.getElementById(id);
        if (element) {
            element.addEventListener('change', modalTriggerAssignmentChecks);
            element.addEventListener('valueChange.mdb.select', modalTriggerAssignmentChecks);
        }
    });
}, 0);
</script>
