import $ from 'jquery';
import axios from 'axios';

const CELL_SIZE = 80;
const CELL_GAP = 16;
const MIN_ZOOM = 0.05;
const MAX_ZOOM = 3;
const ZOOM_FACTOR = 0.1;
const SCROLL_SENSITIVITY = 0.6;
const DAY_IN_MS = 24 * 60 * 60 * 1000;

const ACTIONS_REQUIRING_DATE = new Set(['watering', 'pruning', 'fertilisation', 'pesticide', 'harvest']);

function showToast(message, type = 'error') {
    let container = document.querySelector('[data-role="toast-container"]');
    if (!container) {
        container = document.createElement('div');
        container.dataset.role = 'toast-container';
        container.style.position = 'fixed';
        container.style.top = '1rem';
        container.style.right = '1rem';
        container.style.zIndex = '9999';
        container.style.display = 'flex';
        container.style.flexDirection = 'column';
        container.style.gap = '0.5rem';
        document.body.appendChild(container);
    }

    const toast = document.createElement('div');
    toast.textContent = message;
    toast.style.padding = '0.6rem 0.9rem';
    toast.style.borderRadius = '6px';
    toast.style.boxShadow = '0 4px 12px rgba(0,0,0,0.15)';
    toast.style.color = '#0f172a';
    toast.style.fontSize = '0.9rem';
    toast.style.backgroundColor = type === 'success' ? '#bbf7d0' : '#fee2e2';

    container.appendChild(toast);

    setTimeout(() => {
        toast.remove();
        if (!container.children.length) {
            container.remove();
        }
    }, 3000);
}

function parseData(elementId, fallback) {
    const el = document.getElementById(elementId);
    if (!el) {
        return fallback;
    }

    try {
        return JSON.parse(el.textContent ?? 'null') ?? fallback;
    } catch (error) {
        console.error(`Failed to parse payload for ${elementId}`, error);
        return fallback;
    }
}

function computeContentDimensions(rows) {
    const maxX = Math.max(0, ...rows.map((row) => row.location.x));
    const maxY = Math.max(0, ...rows.map((row) => row.location.y));

    const width = Math.max(1, maxX + 1) * (CELL_SIZE + CELL_GAP);
    const height = Math.max(1, maxY + 1) * (CELL_SIZE + CELL_GAP);

    return { width, height };
}

function applyStatusClass(element, status) {
    const targetStatus = status ?? 'unknown';
    element.classList.forEach((name) => {
        if (name.startsWith('status-')) {
            element.classList.remove(name);
        }
    });
    element.classList.add(`status-${targetStatus}`);
}

function createCell(row) {
    const cell = document.createElement('div');
    cell.classList.add('map-cell');
    cell.dataset.rowId = String(row.id);
    cell.style.left = `${row.location.x * (CELL_SIZE + CELL_GAP)}px`;
    cell.style.top = `${row.location.y * (CELL_SIZE + CELL_GAP)}px`;
    cell.style.width = `${CELL_SIZE}px`;
    cell.style.height = `${CELL_SIZE}px`;

    applyStatusClass(cell, row.status);

    const countBadge = document.createElement('div');
    countBadge.classList.add('map-cell-count');
    countBadge.dataset.role = 'vine-count';
    countBadge.textContent = String(row.vine_count ?? 0);

    const varietyLabel = document.createElement('div');
    varietyLabel.classList.add('map-cell-variety');
    varietyLabel.dataset.role = 'variety-label';
    varietyLabel.textContent = row.variety?.label ?? 'Unassigned';

    const badgeRow = document.createElement('div');
    badgeRow.classList.add('map-cell-badge-row');
    badgeRow.appendChild(countBadge);

    const warning = document.createElement('div');
    warning.classList.add('map-cell-warning');
    warning.dataset.role = 'restriction-warning';
    warning.style.display = 'none';

    const skull = document.createElement('span');
    skull.dataset.role = 'skull-icon';
    skull.textContent = '☠';

    const warningText = document.createElement('span');
    warningText.dataset.role = 'restriction-text';

    warning.appendChild(skull);
    warning.appendChild(warningText);
    badgeRow.appendChild(warning);

    cell.appendChild(badgeRow);
    cell.appendChild(varietyLabel);

    return cell;
}

function parseISODate(value) {
    if (!value || typeof value !== 'string') {
        return null;
    }

    const parts = value.split('-').map((segment) => Number(segment));
    if (parts.length !== 3 || parts.some((number) => Number.isNaN(number))) {
        return null;
    }

    const [year, month, day] = parts;
    return new Date(Date.UTC(year, month - 1, day));
}

function withinDays(laterDate, earlierDate, windowDays) {
    if (!laterDate || !earlierDate) {
        return false;
    }

    const diffDays = (laterDate.getTime() - earlierDate.getTime()) / DAY_IN_MS;
    return diffDays >= 0 && diffDays <= windowDays;
}

function daysBetween(laterDate, earlierDate) {
    if (!laterDate || !earlierDate) {
        return null;
    }

    return Math.round((laterDate.getTime() - earlierDate.getTime()) / DAY_IN_MS);
}

function updateNextState($nextButton, selectedAction, selectedRows, actionDate) {
    const requiresDate = selectedAction ? ACTIONS_REQUIRING_DATE.has(selectedAction) : false;
    const hasDate = !requiresDate || Boolean(actionDate);
    const enable = Boolean(selectedAction) && selectedRows.size > 0 && hasDate;
    $nextButton.prop('disabled', !enable);
}

function activateActionButton($sidebar, actionKey) {
    $sidebar.find('[data-action]').removeClass('active');
    if (actionKey) {
        $sidebar.find(`[data-action="${actionKey}"]`).addClass('active');
    }
}

export default function initMapIndex() {
    const $root = $('[data-page="vineyard-map-index"]');
    if (!$root.length) {
        return;
    }

    const rows = parseData('vineyard-map-rows', []);
    const routes = parseData('vineyard-map-routes', {});

    const selectedRows = new Set();
    const rowElements = new Map();
    const rowDataById = new Map();
    let selectedAction = null;
    let actionPromptTimeout = null;
    let selectedActionDate = '';

    const $sidebar = $root.find('[data-role="sidebar"]');
    const $mapPanel = $root.find('[data-role="map-panel"]');
    const $nextButton = $root.find('[data-control="next-action"]');
    const $clearButton = $root.find('[data-control="clear-selection"]');
    const mapViewport = $root.find('[data-role="map-container"]')[0];
    const actionDetailsSection = $root.find('[data-role="action-details"]')[0] ?? null;
    const actionDateInput = $root.find('[data-control="action-date"]')[0] ?? null;
    const actionTimelineRoot = actionDetailsSection?.querySelector('[data-role="action-timeline"]') ?? null;
    const actionTimelineGroups = actionTimelineRoot
        ? {
              pesticide: {
                  root: actionTimelineRoot.querySelector('[data-action="pesticide"]') ?? null,
                  completed: actionTimelineRoot.querySelector('[data-timeline="pesticide-completed"]') ?? null,
                  planned: actionTimelineRoot.querySelector('[data-timeline="pesticide-planned"]') ?? null,
              },
              harvest: {
                  root: actionTimelineRoot.querySelector('[data-action="harvest"]') ?? null,
                  completed: actionTimelineRoot.querySelector('[data-timeline="harvest-completed"]') ?? null,
                  planned: actionTimelineRoot.querySelector('[data-timeline="harvest-planned"]') ?? null,
              },
          }
        : null;

    if (!mapViewport) {
        return;
    }

    const $helpBtn1 = $sidebar.find('button[aria-label="Help"]');
    const $tooltip1 = $sidebar.find('.action-tooltip');
    if ($helpBtn1.length && $tooltip1.length) {
        $helpBtn1.on('mouseenter focus', function() {
            $tooltip1.show();
        });
        $helpBtn1.on('mouseleave blur', function() {
            $tooltip1.hide();
        });
    }

    const $helpBtn2 = $('button[aria-label="Help2"]');
    const $tooltip2 = $('.action-tooltip2');
    
    
    if ($helpBtn2.length && $tooltip2.length) {
        console.log($helpBtn2, $tooltip2, $helpBtn1);

        $helpBtn2.on('mouseenter focus', function() {
            $tooltip2.show();
        });
        $helpBtn2.on('mouseleave blur', function() {
            $tooltip2.hide();
        });
    }

    

    const actionRequiresDate = (actionKey) => {
        if (!actionKey) {
            return false;
        }

        return ACTIONS_REQUIRING_DATE.has(actionKey);
    };

    const toggleActionDetails = () => {
        if (!actionDetailsSection || !actionDateInput) {
            return;
        }

        const requiresDate = actionRequiresDate(selectedAction);

        if (requiresDate) {
            actionDetailsSection.classList.add('visible');
            actionDetailsSection.setAttribute('aria-hidden', 'false');
            actionDateInput.disabled = false;
            actionDateInput.value = selectedActionDate || '';
        } else {
            actionDetailsSection.classList.remove('visible');
            actionDetailsSection.setAttribute('aria-hidden', 'true');
            actionDateInput.disabled = true;
            actionDateInput.value = '';
            selectedActionDate = '';
        }

        if (actionTimelineRoot) {
            const timelineVisible = selectedAction === 'harvest' || selectedAction === 'pesticide';
            actionTimelineRoot.classList.toggle('visible', timelineVisible);
            actionTimelineRoot.setAttribute('aria-hidden', timelineVisible ? 'false' : 'true');
        }
    };

    toggleActionDetails();
    if (actionDateInput) {
        const today = new Date();
        const year = today.getFullYear();
        const month = String(today.getMonth() + 1).padStart(2, '0');
        const day = String(today.getDate()).padStart(2, '0');
        actionDateInput.min = `${year}-${month}-${day}`;
    }

    const updateTimelineDisplay = () => {
        if (!actionTimelineGroups) {
            return;
        }

        const aggregated = {
            pesticide: {
                completed: new Set(),
                planned: new Set(),
            },
            harvest: {
                completed: new Set(),
                planned: new Set(),
            },
        };

        selectedRows.forEach((rowId) => {
            const row = rowDataById.get(rowId);
            if (!row) {
                return;
            }

            ['pesticide', 'harvest'].forEach((category) => {
                const timeline = row.timeline?.[category];
                if (!timeline) {
                    return;
                }

                (timeline.completed ?? []).forEach((date) => {
                    if (date) {
                        aggregated[category].completed.add(date);
                    }
                });
                (timeline.planned ?? []).forEach((date) => {
                    if (date) {
                        aggregated[category].planned.add(date);
                    }
                });
            });
        });

        const timelineVisible = actionTimelineRoot?.classList.contains('visible');

        ['pesticide', 'harvest'].forEach((category) => {
            const group = actionTimelineGroups[category];
            if (!group) {
                return;
            }

            const completed = Array.from(aggregated[category].completed).sort();
            const planned = Array.from(aggregated[category].planned).sort();

            if (group.completed) {
                group.completed.textContent = completed.length ? completed.join(', ') : 'None';
            }

            if (group.planned) {
                group.planned.textContent = planned.length ? planned.join(', ') : 'None';
            }

            if (group.root) {
                const isRelevant = actionRequiresDate(selectedAction) && (selectedAction === category || selectedAction === 'harvest' || selectedAction === 'pesticide');
                group.root.classList.toggle('active', Boolean(timelineVisible && isRelevant));
            }
        });
    };

    const requireActionBeforeSelection = () => {
        if (selectedAction) {
            return true;
        }

        mapViewport.classList.add('requires-action');
        window.clearTimeout(actionPromptTimeout);
        actionPromptTimeout = window.setTimeout(() => {
            mapViewport.classList.remove('requires-action');
        }, 420);

        return false;
    };

    const { width: contentWidth, height: contentHeight } = computeContentDimensions(rows);

    mapViewport.innerHTML = '';
    const mapContent = document.createElement('div');
    mapContent.classList.add('vineyard-map-grid');
    mapContent.style.width = `${contentWidth}px`;
    mapContent.style.height = `${contentHeight}px`;
    mapViewport.appendChild(mapContent);

    const selectionLayer = document.createElement('div');
    selectionLayer.classList.add('vineyard-map-selection-layer');
    mapViewport.appendChild(selectionLayer);

    rows.forEach((row) => {
        const cell = createCell(row);
        mapContent.appendChild(cell);
        const rowId = Number(row.id);
        rowElements.set(rowId, cell);

        const timeline = row.timeline ?? {};
        rowDataById.set(rowId, {
            ...row,
            timeline: {
                pesticide: {
                    completed: Array.isArray(timeline?.pesticide?.completed) ? timeline.pesticide.completed : [],
                    planned: Array.isArray(timeline?.pesticide?.planned) ? timeline.pesticide.planned : [],
                },
                harvest: {
                    completed: Array.isArray(timeline?.harvest?.completed) ? timeline.harvest.completed : [],
                    planned: Array.isArray(timeline?.harvest?.planned) ? timeline.harvest.planned : [],
                },
            },
        });
    });

    const viewportState = {
        scale: 1,
        translateX: 0,
        translateY: 0,
    };

    let eligibleRows = null;

    const isRowEligibleForAction = (actionKey, row) => {
        if (!row) {
            return false;
        }

        const hasVarietyAssigned = Boolean(row.variety);

        if (['discard-plants', 'harvest', 'watering', 'pruning', 'fertilisation', 'pesticide'].includes(actionKey)) {
            return hasVarietyAssigned;
        }

        if (actionKey === 'add-plants') {
            return !hasVarietyAssigned;
        }

        return true;
    };

    const isRowEligibleOnDate = (actionKey, row, targetDate) => {
        if (!targetDate) {
            return true;
        }

        const timeline = row.timeline ?? {};
        const pesticideDates = [
            ...(timeline.pesticide?.completed ?? []),
            ...(timeline.pesticide?.planned ?? []),
        ]
            .map((value) => parseISODate(value))
            .filter(Boolean);

        const harvestDates = [
            ...(timeline.harvest?.completed ?? []),
            ...(timeline.harvest?.planned ?? []),
        ]
            .map((value) => parseISODate(value))
            .filter(Boolean);

        if (actionKey === 'harvest') {
            return !pesticideDates.some((pesticideDate) => withinDays(targetDate, pesticideDate, 14));
        }

        if (actionKey === 'pesticide') {
            return !harvestDates.some((harvestDate) => withinDays(harvestDate, targetDate, 14));
        }

        return true;
    };

    const computeEligibleRows = (actionKey, actionDate) => {
        if (!actionKey) {
            return null;
        }

        const eligible = new Set();
        const requiresDate = actionRequiresDate(actionKey);
        const targetDate = requiresDate && actionDate ? parseISODate(actionDate) : null;

        rowDataById.forEach((row, rowId) => {
            if (!isRowEligibleForAction(actionKey, row)) {
                return;
            }

            if (requiresDate && targetDate && !isRowEligibleOnDate(actionKey, row, targetDate)) {
                return;
            }

            eligible.add(rowId);
        });

        return eligible;
    };

    const applyRowEligibility = () => {
        let selectionChanged = false;
        rowElements.forEach((element, rowId) => {
            const eligible = !eligibleRows || eligibleRows.has(rowId);
            element.classList.toggle('disabled', !eligible);

            const warning = element.querySelector('[data-role="restriction-warning"]');
            const warningText = element.querySelector('[data-role="restriction-text"]');

            if (warning && warningText) {
                warning.style.display = 'none';
                warning.classList.remove('map-cell-warning--pesticide');

                const row = rowDataById.get(rowId);
                const timeline = row?.timeline ?? {};
                const targetDate = selectedActionDate ? parseISODate(selectedActionDate) : null;

                if (!eligible && selectedAction === 'harvest' && targetDate) {
                    const pesticideDates = [
                        ...(timeline.pesticide?.completed ?? []),
                        ...(timeline.pesticide?.planned ?? []),
                    ]
                        .map((value) => parseISODate(value))
                        .filter(Boolean);

                    if (pesticideDates.length > 0) {
                        let maxRemaining = 0;

                        pesticideDates.forEach((pesticideDate) => {
                            const diff = 14 - daysBetween(targetDate, pesticideDate);
                            if (Number.isFinite(diff) && diff > maxRemaining) {
                                maxRemaining = diff;
                            }
                        });

                        if (maxRemaining > 0) {
                            warningText.textContent = `${maxRemaining}d`;
                            const icon = element.querySelector('[data-role="skull-icon"]');
                            if (icon) {
                                icon.textContent = '☠';
                            }
                            warning.style.display = 'flex';
                        }
                    }
                }

                if (!eligible && selectedAction === 'pesticide' && targetDate) {
                    const harvestDates = [
                        ...(timeline.harvest?.completed ?? []),
                        ...(timeline.harvest?.planned ?? []),
                    ]
                        .map((value) => parseISODate(value))
                        .filter(Boolean);

                    if (harvestDates.length > 0) {
                        let minUntilHarvest = Infinity;

                        harvestDates.forEach((harvestDate) => {
                            const diff = daysBetween(harvestDate, targetDate);
                            if (Number.isFinite(diff) && diff >= 0 && diff < minUntilHarvest) {
                                minUntilHarvest = diff;
                            }
                        });

                        if (Number.isFinite(minUntilHarvest) && minUntilHarvest !== Infinity) {
                            warningText.textContent = `${minUntilHarvest}d`;
                            const icon = element.querySelector('[data-role="skull-icon"]');
                            if (icon) {
                                icon.textContent = '🍇';
                            }
                            warning.classList.add('map-cell-warning--pesticide');
                            warning.style.display = 'flex';
                        }
                    }
                }
            }

            if (!eligible && selectedRows.has(rowId)) {
                selectedRows.delete(rowId);
                selectionChanged = true;
            }
        });

        return selectionChanged;
    };

    eligibleRows = computeEligibleRows(selectedAction, selectedActionDate);
    applyRowEligibility();

    applyRowEligibility();

    const clampTransform = () => {
        const viewportWidth = mapViewport.clientWidth;
        const viewportHeight = mapViewport.clientHeight;
        const scaledWidth = contentWidth * viewportState.scale;
        const scaledHeight = contentHeight * viewportState.scale;

        if (scaledWidth <= viewportWidth) {
            viewportState.translateX = (viewportWidth - scaledWidth) / 2;
        } else {
            const minX = viewportWidth - scaledWidth;
            viewportState.translateX = Math.min(0, Math.max(minX, viewportState.translateX));
        }

        if (scaledHeight <= viewportHeight) {
            viewportState.translateY = (viewportHeight - scaledHeight) / 2;
        } else {
            const minY = viewportHeight - scaledHeight;
            viewportState.translateY = Math.min(0, Math.max(minY, viewportState.translateY));
        }
    };

    const applyTransform = () => {
        clampTransform();
        mapContent.style.transform = `translate(${viewportState.translateX}px, ${viewportState.translateY}px) scale(${viewportState.scale})`;
    };

    const fitContentToViewport = () => {
        if (!contentWidth || !contentHeight) {
            viewportState.scale = 1;
            viewportState.translateX = 0;
            viewportState.translateY = 0;
            applyTransform();
            return;
        }

        const viewportWidth = mapViewport.clientWidth || 1;
        const viewportHeight = mapViewport.clientHeight || 1;
        const scaleX = viewportWidth / contentWidth;
        const scaleY = viewportHeight / contentHeight;
        const fittedScale = Math.min(1, Math.min(scaleX, scaleY));

        viewportState.scale = Math.min(MAX_ZOOM, Math.max(fittedScale, MIN_ZOOM));
        viewportState.translateX = (viewportWidth - contentWidth * viewportState.scale) / 2;
        viewportState.translateY = (viewportHeight - contentHeight * viewportState.scale) / 2;

        applyTransform();
    };

    const updateSelectionStyles = () => {
        rowElements.forEach((element, rowId) => {
            if (selectedRows.has(rowId)) {
                element.classList.add('selected');
            } else {
                element.classList.remove('selected');
            }
        });
    };

    const syncState = () => {
        updateSelectionStyles();
        updateNextState($nextButton, selectedAction, selectedRows, selectedActionDate);
        updateTimelineDisplay();
    };

    if (actionDateInput) {
        actionDateInput.addEventListener('input', (event) => {
            selectedActionDate = event.target.value;

            if (selectedActionDate) {
                const today = new Date();
                today.setHours(0, 0, 0, 0);
                const chosen = parseISODate(selectedActionDate);
                if (chosen && chosen < today) {
                    showToast('Planned date cannot be in the past.', 'error');
                }
            }
            eligibleRows = computeEligibleRows(selectedAction, selectedActionDate);
            applyRowEligibility();
            syncState();
        });
    }

    const clearSelection = () => {
        if (selectedRows.size === 0) {
            return;
        }
        selectedRows.clear();
        syncState();
    };

    const selectRow = (rowId, multi) => {
        if (eligibleRows && !eligibleRows.has(rowId)) {
            return;
        }

        if (!multi) {
            selectedRows.clear();
            selectedRows.add(rowId);
        } else if (selectedRows.has(rowId)) {
            selectedRows.delete(rowId);
        } else {
            selectedRows.add(rowId);
        }

        syncState();
    };

    const areaSelect = (rowIds, merge) => {
        const candidates = eligibleRows
            ? rowIds.filter((rowId) => eligibleRows.has(rowId))
            : rowIds;

        if (candidates.length === 0) {
            if (!merge && rowIds.length === 0) {
                clearSelection();
            }
            return;
        }

        if (!merge) {
            selectedRows.clear();
        }

        candidates.forEach((rowId) => selectedRows.add(rowId));
        syncState();
    };

    const handleWheel = (event) => {
        if (event.ctrlKey) {
            event.preventDefault();

            const previousScale = viewportState.scale;
            const direction = -event.deltaY;
            const scaleDelta = direction > 0 ? 1 + ZOOM_FACTOR : 1 - ZOOM_FACTOR;
            viewportState.scale = Math.min(MAX_ZOOM, Math.max(MIN_ZOOM, viewportState.scale * scaleDelta));

            const zoomRatio = viewportState.scale / previousScale;
            const rect = mapViewport.getBoundingClientRect();
            const offsetX = event.clientX - rect.left;
            const offsetY = event.clientY - rect.top;

            viewportState.translateX = offsetX - (offsetX - viewportState.translateX) * zoomRatio;
            viewportState.translateY = offsetY - (offsetY - viewportState.translateY) * zoomRatio;

            applyTransform();
            return;
        }

        event.preventDefault();

        if (event.shiftKey) {
            viewportState.translateX -= event.deltaY * SCROLL_SENSITIVITY;
            viewportState.translateX -= event.deltaX * SCROLL_SENSITIVITY;
        } else {
            viewportState.translateY -= event.deltaY * SCROLL_SENSITIVITY;
            viewportState.translateX -= event.deltaX * SCROLL_SENSITIVITY;
        }

        applyTransform();
    };

    let isSelectingArea = false;
    let selectionStartPoint = null;
    let selectionBox = null;
    let selectionMerge = false;
    let selectionMoved = false;
    let skipClickClear = false;

    const beginSelection = (event) => {
        if (event.button !== 0) {
            return;
        }

        const cell = event.target.closest('[data-row-id]');
        if (cell && !cell.classList.contains('disabled')) {
            return;
        }

        if (!requireActionBeforeSelection()) {
            return;
        }

        isSelectingArea = true;
        selectionMerge = event.shiftKey;
        selectionMoved = false;
        selectionStartPoint = { x: event.clientX, y: event.clientY };
        selectionBox = document.createElement('div');
        selectionBox.classList.add('selection-box');
        selectionLayer.appendChild(selectionBox);
        event.preventDefault();
    };

    const updateSelectionBox = (event) => {
        if (!isSelectingArea || !selectionBox || !selectionStartPoint) {
            return;
        }

        const rect = mapViewport.getBoundingClientRect();
        const startX = selectionStartPoint.x - rect.left;
        const startY = selectionStartPoint.y - rect.top;
        const currentX = event.clientX - rect.left;
        const currentY = event.clientY - rect.top;

        const left = Math.min(startX, currentX);
        const top = Math.min(startY, currentY);
        const width = Math.abs(currentX - startX);
        const height = Math.abs(currentY - startY);

        if (!selectionMoved && (width > 3 || height > 3)) {
            selectionMoved = true;
        }

        Object.assign(selectionBox.style, {
            left: `${left}px`,
            top: `${top}px`,
            width: `${width}px`,
            height: `${height}px`,
        });
    };

    const finishSelection = (moved) => {
        if (!isSelectingArea) {
            return;
        }

        const boxRect = selectionBox?.getBoundingClientRect() ?? null;

        selectionBox?.remove();
        selectionBox = null;
        selectionStartPoint = null;
        isSelectingArea = false;

        if (!moved) {
            skipClickClear = true;
            if (!selectionMerge) {
                clearSelection();
            }
            selectionMerge = false;
            return;
        }

        if (!boxRect) {
            selectionMerge = false;
            return;
        }

        const intersectingRows = [];
        rowElements.forEach((element, rowId) => {
            const cellRect = element.getBoundingClientRect();
            const fullyContained =
                cellRect.left >= boxRect.left &&
                cellRect.right <= boxRect.right &&
                cellRect.top >= boxRect.top &&
                cellRect.bottom <= boxRect.bottom;

            if (fullyContained) {
                intersectingRows.push(rowId);
            }
        });

        skipClickClear = true;
        areaSelect(intersectingRows, selectionMerge);
        selectionMerge = false;
    };

    mapContent.addEventListener('click', (event) => {
        const cell = event.target.closest('[data-row-id]');
        if (!cell) {
            return;
        }

        if (!requireActionBeforeSelection()) {
            return;
        }

        if (cell.classList.contains('disabled')) {
            skipClickClear = true;
            event.stopPropagation();
            return;
        }

        skipClickClear = true;
        event.stopPropagation();

        const rowId = Number(cell.dataset.rowId);
        if (!Number.isFinite(rowId) || rowId <= 0) {
            return;
        }

        selectRow(rowId, event.shiftKey);
    });

    mapViewport.addEventListener('click', (event) => {
        if (skipClickClear) {
            skipClickClear = false;
            return;
        }

        if (event.target.closest('[data-row-id]')) {
            return;
        }

        if (!event.shiftKey) {
            clearSelection();
        }
    });

    mapViewport.addEventListener('mousedown', beginSelection);
    window.addEventListener('mousemove', updateSelectionBox);
    window.addEventListener('mouseup', () => {
        const wasSelecting = isSelectingArea;
        const moved = selectionMoved;
        finishSelection(moved);
        if (wasSelecting && moved) {
            skipClickClear = true;
        }
        selectionMoved = false;
    });

    mapViewport.addEventListener('wheel', handleWheel, { passive: false });

    window.addEventListener('resize', applyTransform);
    fitContentToViewport();

    $sidebar.on('click', '[data-action]', function () {
        const $button = $(this);
        const action = $button.data('action');

        if (selectedAction === action) {
            selectedAction = null;
            activateActionButton($sidebar, null);
        } else {
            selectedAction = action;
            activateActionButton($sidebar, action);
        }

        if (selectedAction) {
            mapViewport.classList.remove('requires-action');
        }

        toggleActionDetails();

        eligibleRows = computeEligibleRows(selectedAction, selectedActionDate);
        applyRowEligibility();

        syncState();
    });
    $clearButton.on('click', () => {
        clearSelection();
    });

    $nextButton.on('click', async (event) => {
        event.preventDefault();

        if (!selectedAction || selectedRows.size === 0) {
            return;
        }

        const rowList = Array.from(selectedRows.values());

        if (selectedAction === 'discard-plants') {
            const endpoint = routes['discard-plants'];
            if (!endpoint) {
                return;
            }

            try {
                await axios.post(endpoint, { rows: rowList });

                rowList.forEach((rowId) => {
                    const element = rowElements.get(rowId);
                    if (!element) {
                        return;
                    }

                    element.classList.remove('selected');
                    // Backend now marks rows inactive but keeps vine counts intact.
                    applyStatusClass(element, 'inactive');

                    const data = rowDataById.get(rowId) ?? {};
                    rowDataById.set(rowId, {
                        ...data,
                        status: 'inactive',
                        variety: null,
                        variety_variation_id: null,
                    });

                    const varietyLabel = element.querySelector('[data-role="variety-label"]');
                    if (varietyLabel) {
                        varietyLabel.textContent = 'Unassigned';
                    }
                });

                eligibleRows = computeEligibleRows(selectedAction, selectedActionDate);
                applyRowEligibility();
                selectedRows.clear();
                syncState();
            } catch (error) {
                console.error('Failed to discard plants', error);
                alert('Discard action failed. Please try again.');
            }

            return;
        }

        const endpoint = routes[selectedAction];
        if (!endpoint) {
            return;
        }

        if (actionRequiresDate(selectedAction) && !selectedActionDate) {
            return;
        }

        const url = new URL(endpoint, window.location.origin);
        url.searchParams.set('rows', rowList.join(','));
        if (actionRequiresDate(selectedAction)) {
            url.searchParams.set('date', selectedActionDate);
        }
        window.location.href = url.toString();
    });

    syncState();
}
