"use client";

import React, { memo, useCallback, useRef, useState } from "react";
import {
  Box,
  Button,
  Chip,
  Dialog,
  DialogActions,
  DialogContent,
  DialogTitle,
  IconButton,
  Paper,
  Stack,
  TextField,
  Tooltip,
  Typography,
} from "@mui/material";
import { alpha } from "@mui/material/styles";
import { MapPin, Move, Pencil, Plus, Trash2, Undo2, X } from "lucide-react";
import { useTranslation } from "react-i18next";
import type {
  PlannedLane,
  PlannedShop,
  ScheduleDragWorkspaceState,
} from "@/components/Shop/scheduleDragWorkspace";
import type { ScheduleLaneOption } from "@/components/Shop/ScheduleChangeModal";
import {
  buildLaneDistrictSections,
  districtDisplayExistsInShops,
  formatShopCardSubtitle,
} from "@/components/Shop/routeBoardDisplayOrder";

type Props = {
  lanes: ScheduleLaneOption[];
  leftLaneId: string;
  rightLaneId: string;
  plannedLanes: ScheduleDragWorkspaceState;
  pendingEmptyDistrictsByLane: Record<string, string[]>;
  onLeftLaneChange: (laneId: string) => void;
  onRightLaneChange: (laneId: string) => void;
  onMoveShop: (
    shopId: number,
    fromLaneId: string,
    toLaneId: string,
    beforeShopId: number | null,
    targetDistrict?: string | null,
  ) => void;
  onRevertShop: (shopId: number, currentLaneId: string) => void;
  onSetLoadingSequence: (
    laneId: string,
    shopId: number,
    loadingSequence: number,
  ) => void;
  onAddEmptyDistrict: (laneId: string, display: string) => void;
  onRemoveEmptyDistrict: (laneId: string, display: string) => void;
  onDeleteShop: (shopId: number, laneId: string) => void;
  onAddShop: (laneId: string) => void;
  onSetLaneDepartureTime: (laneId: string, departureTime: string) => boolean;
};

function toTimeInputValue(t: string | undefined): string {
  const s = String(t ?? "").trim();
  if (!s) return "00:00";
  const m = s.match(/^(\d{1,2}):(\d{2})(?::\d{2})?/);
  if (m) return `${m[1].padStart(2, "0")}:${m[2]}`;
  return "00:00";
}

function formatDepartureChip(t: string): string {
  const m = String(t ?? "").trim().match(/^(\d{1,2}):(\d{2})/);
  return m ? `${m[1].padStart(2, "0")}:${m[2]}` : "-";
}

function getBeforeShopIdByPointer(
  laneId: string,
  clientY: number,
): number | null {
  const laneEl = document.querySelector<HTMLElement>(
    `[data-schedule-lane-id="${CSS.escape(laneId)}"]`,
  );
  if (!laneEl) return null;
  const cards = Array.from(
    laneEl.querySelectorAll<HTMLElement>("[data-schedule-shop-id]"),
  );
  for (const cardEl of cards) {
    const rect = cardEl.getBoundingClientRect();
    const midY = rect.top + rect.height / 2;
    if (clientY < midY) {
      const idStr = cardEl.getAttribute("data-schedule-shop-id");
      const id = idStr ? Number(idStr) : NaN;
      return Number.isFinite(id) ? id : null;
    }
  }
  return null;
}

const nativeSelectSx = {
  fontSize: "0.75rem",
  fontWeight: 600,
  py: 0.5,
  px: 1,
  borderRadius: 1,
  border: "1px solid",
  borderColor: "divider",
  bgcolor: "background.paper",
  flex: 1,
  minWidth: 0,
  width: "100%",
  maxWidth: "100%",
} as const;

const laneSelectorLabelSx = {
  fontWeight: 700,
  color: "text.secondary",
  flexShrink: 0,
  minWidth: "4.5em",
  whiteSpace: "nowrap",
} as const;

type SeqEditTarget = {
  laneId: string;
  shopId: number;
  draft: string;
};

const LoadingSequenceDisplay = memo(function LoadingSequenceDisplay({
  loadingSequence,
  seqLabel,
  editAriaLabel,
  onEdit,
}: {
  loadingSequence: number;
  seqLabel: (seq: number) => string;
  editAriaLabel: string;
  onEdit: () => void;
}) {
  return (
    <Stack direction="row" spacing={0.25} alignItems="center">
      <Chip
        size="small"
        label={seqLabel(loadingSequence)}
        sx={{
          height: 22,
          fontSize: "0.65rem",
          fontWeight: 800,
          bgcolor: "primary.50",
          color: "primary.dark",
        }}
      />
      <IconButton
        size="small"
        aria-label={editAriaLabel}
        onMouseDown={(e) => e.stopPropagation()}
        onClick={(e) => {
          e.stopPropagation();
          onEdit();
        }}
        sx={{
          width: 22,
          height: 22,
          color: "primary.main",
          flexShrink: 0,
        }}
      >
        <Pencil size={12} />
      </IconButton>
    </Stack>
  );
});

const DistrictSectionHeader = memo(function DistrictSectionHeader({
  district,
  count,
  isPendingEmpty,
  editAriaLabel,
  removeAriaLabel,
  onRemove,
}: {
  district: string;
  count: number;
  isPendingEmpty?: boolean;
  editAriaLabel?: string;
  removeAriaLabel?: string;
  onRemove?: () => void;
}) {
  return (
    <Stack
      direction="row"
      spacing={0.5}
      alignItems="center"
      sx={{ px: 0.5, mb: 1, minWidth: 0 }}
    >
      <MapPin size={14} />
      <Typography
        variant="caption"
        sx={{ fontWeight: 900, color: "text.secondary", minWidth: 0 }}
        noWrap
      >
        {district}
      </Typography>
      <Box sx={{ flex: 1, height: 1, bgcolor: "grey.200", mx: 0.5 }} />
      <Typography variant="caption" color="text.secondary">
        {count}
      </Typography>
      {isPendingEmpty && onRemove && (
        <IconButton
          size="small"
          sx={{ p: 0.25 }}
          onMouseDown={(e) => e.stopPropagation()}
          onClick={(e) => {
            e.stopPropagation();
            onRemove();
          }}
          aria-label={removeAriaLabel ?? editAriaLabel}
        >
          <X size={14} />
        </IconButton>
      )}
    </Stack>
  );
});

const PlannedShopCard = memo(function PlannedShopCard({
  shop,
  laneId,
  isMoved,
  isHovered,
  hoveredPosition,
  movedBadgeLabel,
  seqLabel,
  seqEditAriaLabel,
  onDragStartShop,
  onDragOverShop,
  onDropShop,
  onRevertShop,
  onDeleteShop,
  removeFromLaneTooltip,
  onStartSeqEdit,
}: {
  shop: PlannedShop;
  laneId: string;
  isMoved: boolean;
  isHovered: boolean;
  hoveredPosition: "before" | "after";
  movedBadgeLabel: string;
  seqLabel: (seq: number) => string;
  seqEditAriaLabel: string;
  removeFromLaneTooltip: string;
  onDragStartShop: (shopId: number) => void;
  onDragOverShop: (e: React.DragEvent, shopId: number) => void;
  onDropShop: (e: React.DragEvent, shopId: number) => void;
  onRevertShop: (shopId: number) => void;
  onDeleteShop: (shopId: number, laneId: string) => void;
  onStartSeqEdit: (laneId: string, shopId: number, current: number) => void;
}) {
  return (
    <Box sx={{ position: "relative" }}>
      {isHovered && hoveredPosition === "before" && (
        <Box
          sx={{
            height: 4,
            bgcolor: "primary.main",
            borderRadius: 1,
            mb: 0.5,
          }}
        />
      )}
      <Paper
        variant="outlined"
        draggable
        data-schedule-shop-id={shop.truckRowId}
        onDragStart={() => onDragStartShop(shop.truckRowId)}
        onDragOver={(e) => onDragOverShop(e, shop.truckRowId)}
        onDrop={(e) => onDropShop(e, shop.truckRowId)}
        sx={{
          p: 1.25,
          cursor: "grab",
          bgcolor: isMoved ? alpha("#ed6c02", 0.06) : "background.paper",
          borderColor: isHovered
            ? "primary.main"
            : isMoved
              ? "warning.light"
              : "divider",
          borderStyle: isHovered ? "dashed" : "solid",
          "&:active": { cursor: "grabbing" },
        }}
      >
        <Stack
          direction="row"
          justifyContent="space-between"
          alignItems="flex-start"
          spacing={1}
        >
          <Box sx={{ minWidth: 0 }}>
            <Typography variant="subtitle2" sx={{ fontWeight: 900 }} noWrap>
              {shop.displayName}
            </Typography>
            <Typography
              variant="caption"
              color="text.secondary"
              display="block"
              noWrap
            >
              {formatShopCardSubtitle(shop)}
            </Typography>
          </Box>
          <Stack direction="row" spacing={0.25} alignItems="center" sx={{ flexShrink: 0 }}>
            {isMoved && (
              <IconButton
                size="small"
                onMouseDown={(e) => e.stopPropagation()}
                onClick={(e) => {
                  e.stopPropagation();
                  onRevertShop(shop.truckRowId);
                }}
                sx={{ color: "warning.dark" }}
                aria-label="revert"
              >
                <Undo2 size={14} />
              </IconButton>
            )}
            <Tooltip title={removeFromLaneTooltip}>
              <span>
                <IconButton
                  size="small"
                  onMouseDown={(e) => e.stopPropagation()}
                  onClick={(e) => {
                    e.stopPropagation();
                    onDeleteShop(shop.truckRowId, laneId);
                  }}
                  sx={{ color: "error.main" }}
                  aria-label={removeFromLaneTooltip}
                >
                  <Trash2 size={14} />
                </IconButton>
              </span>
            </Tooltip>
          </Stack>
        </Stack>
        <Stack
          direction="row"
          justifyContent="space-between"
          alignItems="center"
          sx={{ mt: 1 }}
        >
          <LoadingSequenceDisplay
            loadingSequence={shop.loadingSequence}
            seqLabel={seqLabel}
            editAriaLabel={seqEditAriaLabel}
            onEdit={() =>
              onStartSeqEdit(laneId, shop.truckRowId, shop.loadingSequence)
            }
          />
          {isMoved && (
            <Chip
              size="small"
              label={movedBadgeLabel}
              color="warning"
              sx={{ height: 20, fontSize: "0.6rem", fontWeight: 800 }}
            />
          )}
        </Stack>
      </Paper>
      {isHovered && hoveredPosition === "after" && (
        <Box
          sx={{
            height: 4,
            bgcolor: "primary.main",
            borderRadius: 1,
            mt: 0.5,
          }}
        />
      )}
    </Box>
  );
});

const LaneColumn = memo(function LaneColumn({
  lane,
  pendingEmptyDistricts,
  isOver,
  onDragOverColumn,
  onDragLeaveColumn,
  onDropColumn,
  onDropDistrict,
  onDragStartShop,
  onDragOverShop,
  onDropShop,
  onRevertShop,
  onDeleteShop,
  onAddDistrict,
  onRemoveEmptyDistrict,
  hoveredShopId,
  hoveredPosition,
  movedBadgeLabel,
  seqLabel,
  seqEditAriaLabel,
  removeFromLaneTooltip,
  removeEmptyDistrictAriaLabel,
  onStartSeqEdit,
  dropHint,
  addDistrictLabel,
  addShopLabel,
  onAddShop,
  departureLabel,
  departureEditAriaLabel,
  onStartDepartureEdit,
}: {
  lane: PlannedLane;
  pendingEmptyDistricts: string[];
  isOver: boolean;
  onDragOverColumn: (e: React.DragEvent) => void;
  onDragLeaveColumn: () => void;
  onDropColumn: (e: React.DragEvent) => void;
  onDropDistrict: (e: React.DragEvent, district: string) => void;
  onDragStartShop: (shopId: number) => void;
  onDragOverShop: (e: React.DragEvent, shopId: number) => void;
  onDropShop: (e: React.DragEvent, shopId: number) => void;
  onRevertShop: (shopId: number) => void;
  onDeleteShop: (shopId: number, laneId: string) => void;
  onAddDistrict: () => void;
  onRemoveEmptyDistrict: (district: string) => void;
  removeFromLaneTooltip: string;
  hoveredShopId: number | null;
  hoveredPosition: "before" | "after";
  movedBadgeLabel: string;
  seqLabel: (seq: number) => string;
  seqEditAriaLabel: string;
  removeEmptyDistrictAriaLabel: string;
  onStartSeqEdit: (laneId: string, shopId: number, current: number) => void;
  dropHint: string;
  addDistrictLabel: string;
  addShopLabel: string;
  onAddShop: () => void;
  departureLabel: string;
  departureEditAriaLabel: string;
  onStartDepartureEdit: (laneId: string, current: string) => void;
}) {
  const districtSections = buildLaneDistrictSections(
    lane.shops,
    pendingEmptyDistricts,
  );
  return (
    <Paper
      variant="outlined"
      data-schedule-lane-id={lane.id}
      onDragOver={onDragOverColumn}
      onDragLeave={onDragLeaveColumn}
      onDrop={onDropColumn}
      sx={{
        display: "flex",
        flexDirection: "column",
        minHeight: 0,
        height: "100%",
        overflow: "hidden",
        bgcolor: isOver ? alpha("#1976d2", 0.04) : "grey.50",
        borderColor: isOver ? "primary.main" : "divider",
        boxShadow: isOver ? `0 0 0 2px ${alpha("#1976d2", 0.12)}` : undefined,
        transition: "border-color 0.15s, background-color 0.15s",
      }}
    >
      <Box
        sx={{
          px: 1.5,
          py: 1.25,
          borderBottom: 1,
          borderColor: "divider",
          bgcolor: "background.paper",
          display: "flex",
          alignItems: "center",
          justifyContent: "space-between",
          flexShrink: 0,
        }}
      >
        <Box sx={{ minWidth: 0, flex: 1 }}>
          <Stack direction="row" spacing={0.75} alignItems="center" flexWrap="wrap">
            <Typography variant="body2" sx={{ fontWeight: 800 }} noWrap>
              {lane.label}
            </Typography>
            <Chip
              label={lane.truckLanceCode}
              size="small"
              sx={{ height: 18, fontSize: "0.65rem", fontFamily: "monospace" }}
            />
          </Stack>
          <Stack direction="row" alignItems="center" spacing={0.25} sx={{ mt: 0.35 }}>
            <Typography
              variant="caption"
              sx={{
                fontWeight: 700,
                bgcolor: "grey.100",
                px: 0.75,
                py: 0.2,
                borderRadius: 1,
                whiteSpace: "nowrap",
              }}
            >
              {departureLabel}: {formatDepartureChip(lane.departureTime)}
            </Typography>
            <Tooltip title={departureEditAriaLabel}>
              <IconButton
                size="small"
                onClick={(e) => {
                  e.stopPropagation();
                  onStartDepartureEdit(lane.id, lane.departureTime);
                }}
                aria-label={departureEditAriaLabel}
              >
                <Pencil size={14} />
              </IconButton>
            </Tooltip>
          </Stack>
        </Box>
        <Chip
          size="small"
          label={lane.shops.length}
          sx={{ fontWeight: 800, flexShrink: 0 }}
        />
      </Box>

      <Box sx={{ flex: 1, overflow: "auto", p: 1.5 }}>
        {lane.shops.length === 0 && districtSections.length === 0 ? (
          <Stack spacing={1.5}>
            <Box
              sx={{
                minHeight: 120,
                display: "flex",
                flexDirection: "column",
                alignItems: "center",
                justifyContent: "center",
                border: 2,
                borderStyle: "dashed",
                borderColor: "divider",
                borderRadius: 2,
                color: "text.secondary",
                bgcolor: alpha("#fff", 0.6),
              }}
            >
              <Move size={20} strokeWidth={1.25} />
              <Typography variant="caption" sx={{ mt: 0.5, textAlign: "center" }}>
                {dropHint}
              </Typography>
            </Box>
            <Button
              size="small"
              variant="text"
              startIcon={<Plus size={14} />}
              onClick={onAddDistrict}
              sx={{ alignSelf: "flex-start" }}
            >
              {addDistrictLabel}
            </Button>
          </Stack>
        ) : (
          <Stack spacing={2}>
            {districtSections.map(({ district, shops, isPendingEmpty }) => (
              <Box
                key={`${lane.id}::${district}`}
                onDragOver={(e) => {
                  e.preventDefault();
                  onDragOverColumn(e);
                }}
                onDrop={(e) => onDropDistrict(e, district)}
              >
                <DistrictSectionHeader
                  district={district}
                  count={shops.length}
                  isPendingEmpty={isPendingEmpty}
                  removeAriaLabel={removeEmptyDistrictAriaLabel}
                  onRemove={
                    isPendingEmpty
                      ? () => onRemoveEmptyDistrict(district)
                      : undefined
                  }
                />
                <Stack spacing={1}>
                  {shops.map((shop) => (
                    <PlannedShopCard
                      key={shop.truckRowId}
                      shop={shop}
                      laneId={lane.id}
                      isMoved={shop.originalLaneId !== lane.id}
                      isHovered={hoveredShopId === shop.truckRowId}
                      hoveredPosition={hoveredPosition}
                      movedBadgeLabel={movedBadgeLabel}
                      seqLabel={seqLabel}
                      seqEditAriaLabel={seqEditAriaLabel}
                      onDragStartShop={onDragStartShop}
                      onDragOverShop={onDragOverShop}
                      onDropShop={onDropShop}
                      onRevertShop={onRevertShop}
                      onDeleteShop={onDeleteShop}
                      removeFromLaneTooltip={removeFromLaneTooltip}
                      onStartSeqEdit={onStartSeqEdit}
                    />
                  ))}
                </Stack>
              </Box>
            ))}
            <Button
              size="small"
              variant="text"
              startIcon={<Plus size={14} />}
              onClick={onAddDistrict}
              sx={{ alignSelf: "flex-start" }}
            >
              {addDistrictLabel}
            </Button>
          </Stack>
        )}
      </Box>

      <Box
        sx={{
          px: 1.5,
          py: 1,
          borderTop: 1,
          borderColor: "divider",
          bgcolor: "background.paper",
          flexShrink: 0,
        }}
      >
        <Button
          fullWidth
          size="small"
          variant="outlined"
          startIcon={<Plus size={14} />}
          onClick={onAddShop}
          sx={{ textTransform: "none", fontWeight: 700 }}
        >
          {addShopLabel}
        </Button>
      </Box>
    </Paper>
  );
});

const ScheduleDragWorkspacePane: React.FC<Props> = ({
  lanes,
  leftLaneId,
  rightLaneId,
  plannedLanes,
  pendingEmptyDistrictsByLane,
  onLeftLaneChange,
  onRightLaneChange,
  onMoveShop,
  onRevertShop,
  onSetLoadingSequence,
  onAddEmptyDistrict,
  onRemoveEmptyDistrict,
  onDeleteShop,
  onAddShop,
  onSetLaneDepartureTime,
}) => {
  const { t } = useTranslation("shop");
  const draggedRef = useRef<{ shopId: number; fromLaneId: string } | null>(
    null,
  );
  const [isOverColumnId, setIsOverColumnId] = useState<string | null>(null);
  const [hoveredShopId, setHoveredShopId] = useState<number | null>(null);
  const [hoveredPosition, setHoveredPosition] = useState<"before" | "after">(
    "after",
  );
  const [seqEditTarget, setSeqEditTarget] = useState<SeqEditTarget | null>(
    null,
  );
  const [districtAddLaneId, setDistrictAddLaneId] = useState<string | null>(
    null,
  );
  const [districtAddDraft, setDistrictAddDraft] = useState("");
  const [districtAddError, setDistrictAddError] = useState<string | null>(
    null,
  );
  const [departureEditTarget, setDepartureEditTarget] = useState<{
    laneId: string;
    draft: string;
  } | null>(null);
  const [departureEditError, setDepartureEditError] = useState<string | null>(
    null,
  );

  const applyDepartureEdit = useCallback(() => {
    if (!departureEditTarget) return;
    const ok = onSetLaneDepartureTime(
      departureEditTarget.laneId,
      departureEditTarget.draft,
    );
    if (!ok) {
      setDepartureEditError(t("route_err_departure"));
      return;
    }
    setDepartureEditError(null);
    setDepartureEditTarget(null);
  }, [departureEditTarget, onSetLaneDepartureTime, t]);

  const leftLane = plannedLanes.find((l) => l.id === leftLaneId);
  const rightLane = plannedLanes.find((l) => l.id === rightLaneId);

  const clearDrag = useCallback(() => {
    draggedRef.current = null;
    setIsOverColumnId(null);
    setHoveredShopId(null);
  }, []);

  const handleDragStart = (shopId: number, fromLaneId: string) => {
    draggedRef.current = { shopId, fromLaneId };
  };

  const handleDragOverShop = (e: React.DragEvent, shopId: number) => {
    e.preventDefault();
    e.stopPropagation();
    const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
    const offset = e.clientY - rect.top;
    setHoveredShopId(shopId);
    setHoveredPosition(offset < rect.height / 2 ? "before" : "after");
  };

  const handleDropOnShop = (
    e: React.DragEvent,
    targetLaneId: string,
    targetShopId: number,
  ) => {
    e.preventDefault();
    e.stopPropagation();
    const dragged = draggedRef.current;
    if (!dragged) return;
    const lane = plannedLanes.find((l) => l.id === targetLaneId);
    const flat = lane?.shops ?? [];
    let beforeShopId: number | null;
    if (hoveredShopId === targetShopId && hoveredPosition === "before") {
      beforeShopId = targetShopId;
    } else if (hoveredShopId === targetShopId && hoveredPosition === "after") {
      const idx = flat.findIndex((s) => s.truckRowId === targetShopId);
      beforeShopId =
        idx >= 0 && idx < flat.length - 1
          ? (flat[idx + 1]?.truckRowId ?? null)
          : null;
    } else {
      beforeShopId = targetShopId;
    }
    onMoveShop(
      dragged.shopId,
      dragged.fromLaneId,
      targetLaneId,
      beforeShopId,
    );
    clearDrag();
  };

  const handleDropOnColumn = (e: React.DragEvent, targetLaneId: string) => {
    e.preventDefault();
    const dragged = draggedRef.current;
    if (!dragged) return;
    const beforeShopId = getBeforeShopIdByPointer(targetLaneId, e.clientY);
    onMoveShop(
      dragged.shopId,
      dragged.fromLaneId,
      targetLaneId,
      beforeShopId,
    );
    clearDrag();
  };

  const handleDropOnDistrict = useCallback(
    (e: React.DragEvent, targetLaneId: string, district: string) => {
      e.preventDefault();
      e.stopPropagation();
      const dragged = draggedRef.current;
      if (!dragged) return;
      onMoveShop(
        dragged.shopId,
        dragged.fromLaneId,
        targetLaneId,
        null,
        district,
      );
      clearDrag();
    },
    [onMoveShop, clearDrag],
  );

  const renderLaneSelectors = (
    <Box
      sx={{
        p: 1,
        bgcolor: "grey.100",
        borderRadius: 2,
        flexShrink: 0,
        display: "grid",
        gridTemplateColumns: "1fr 1fr",
        columnGap: 1.5,
        alignItems: "center",
      }}
    >
      <Stack
        direction="row"
        spacing={0.75}
        alignItems="center"
        sx={{ minWidth: 0 }}
      >
        <Typography variant="caption" sx={laneSelectorLabelSx}>
          {t("schedule_lane_left")}
        </Typography>
        <Box
          component="select"
          value={leftLaneId}
          onChange={(e: React.ChangeEvent<HTMLSelectElement>) =>
            onLeftLaneChange(e.target.value)
          }
          sx={nativeSelectSx}
        >
          {lanes.map((l) => (
            <option key={l.id} value={l.id} disabled={l.id === rightLaneId}>
              {l.label}
            </option>
          ))}
        </Box>
      </Stack>
      <Stack
        direction="row"
        spacing={0.75}
        alignItems="center"
        sx={{ minWidth: 0 }}
      >
        <Typography variant="caption" sx={laneSelectorLabelSx}>
          {t("schedule_lane_right")}
        </Typography>
        <Box
          component="select"
          value={rightLaneId}
          onChange={(e: React.ChangeEvent<HTMLSelectElement>) =>
            onRightLaneChange(e.target.value)
          }
          sx={nativeSelectSx}
        >
          {lanes.map((l) => (
            <option key={l.id} value={l.id} disabled={l.id === leftLaneId}>
              {l.label}
            </option>
          ))}
        </Box>
      </Stack>
    </Box>
  );

  const seqLabel = (seq: number) =>
    t("schedule_drag_seq", { seq: String(seq) });

  const handleStartSeqEdit = useCallback(
    (laneId: string, shopId: number, current: number) => {
      setSeqEditTarget({
        laneId,
        shopId,
        draft: String(current),
      });
    },
    [],
  );

  const handleCommitSeqEdit = useCallback(() => {
    setSeqEditTarget((prev) => {
      if (!prev) return null;
      const n = Number(prev.draft);
      const seq = Number.isFinite(n) ? Math.max(0, Math.trunc(n)) : 0;
      onSetLoadingSequence(prev.laneId, prev.shopId, seq);
      return null;
    });
  }, [onSetLoadingSequence]);

  const handleCancelSeqEdit = useCallback(() => {
    setSeqEditTarget(null);
  }, []);

  const openDistrictAdd = useCallback((laneId: string) => {
    setDistrictAddLaneId(laneId);
    setDistrictAddDraft("");
    setDistrictAddError(null);
  }, []);

  const closeDistrictAdd = useCallback(() => {
    setDistrictAddLaneId(null);
    setDistrictAddDraft("");
    setDistrictAddError(null);
  }, []);

  const applyDistrictAdd = useCallback(() => {
    if (!districtAddLaneId) return;
    const lane = plannedLanes.find((l) => l.id === districtAddLaneId);
    if (!lane) {
      closeDistrictAdd();
      return;
    }
    const trimmed = districtAddDraft.trim();
    if (!trimmed) {
      setDistrictAddError(t("district_err_name"));
      return;
    }
    if (trimmed === "未分類") {
      setDistrictAddError(t("district_err_reserved"));
      return;
    }
    const pendingExtra = pendingEmptyDistrictsByLane[districtAddLaneId] ?? [];
    if (districtDisplayExistsInShops(lane.shops, pendingExtra, trimmed)) {
      setDistrictAddError(t("district_err_exists"));
      return;
    }
    onAddEmptyDistrict(districtAddLaneId, trimmed);
    closeDistrictAdd();
  }, [
    closeDistrictAdd,
    districtAddDraft,
    districtAddLaneId,
    onAddEmptyDistrict,
    pendingEmptyDistrictsByLane,
    plannedLanes,
    t,
  ]);

  if (!leftLane || !rightLane) {
    return (
      <Stack spacing={1.5} sx={{ flex: 1, minHeight: 0 }}>
        {renderLaneSelectors}
        <Typography variant="body2" color="text.secondary" sx={{ p: 2 }}>
          {t("schedule_no_shops")}
        </Typography>
      </Stack>
    );
  }

  const columnProps = (lane: PlannedLane) => ({
    lane,
    pendingEmptyDistricts: pendingEmptyDistrictsByLane[lane.id] ?? [],
    isOver: isOverColumnId === lane.id,
    onDragOverColumn: (e: React.DragEvent) => {
      e.preventDefault();
      setIsOverColumnId(lane.id);
    },
    onDragLeaveColumn: () => setIsOverColumnId(null),
    onDropColumn: (e: React.DragEvent) => handleDropOnColumn(e, lane.id),
    onDropDistrict: (e: React.DragEvent, district: string) =>
      handleDropOnDistrict(e, lane.id, district),
    onDragStartShop: (shopId: number) => handleDragStart(shopId, lane.id),
    onDragOverShop: handleDragOverShop,
    onDropShop: (e: React.DragEvent, shopId: number) =>
      handleDropOnShop(e, lane.id, shopId),
    onRevertShop: (shopId: number) => onRevertShop(shopId, lane.id),
    onDeleteShop,
    removeFromLaneTooltip: t("tooltip_removeFromLane"),
    onAddDistrict: () => openDistrictAdd(lane.id),
    onRemoveEmptyDistrict: (district: string) =>
      onRemoveEmptyDistrict(lane.id, district),
    hoveredShopId,
    hoveredPosition,
    movedBadgeLabel: t("schedule_moved_badge"),
    seqLabel,
    seqEditAriaLabel: t("schedule_seq_edit_btn"),
    removeEmptyDistrictAriaLabel: t("aria_removeEmptyDistrict"),
    onStartSeqEdit: handleStartSeqEdit,
    dropHint: t("schedule_drop_hint"),
    addDistrictLabel: t("btn_addDistrict"),
    addShopLabel: t("btn_addShopToLane"),
    onAddShop: () => onAddShop(lane.id),
    departureLabel: t("Departure"),
    departureEditAriaLabel: t("departureTooltipEditSave"),
    onStartDepartureEdit: (laneId: string, current: string) => {
      setDepartureEditError(null);
      setDepartureEditTarget({
        laneId,
        draft: toTimeInputValue(current),
      });
    },
  });

  return (
    <>
      <Stack spacing={1.5} sx={{ flex: 1, minHeight: 0 }}>
        {renderLaneSelectors}
        <Box
          sx={{
            flex: 1,
            minHeight: 0,
            display: "grid",
            gridTemplateColumns: "1fr 1fr",
            gap: 1.5,
          }}
        >
          <LaneColumn {...columnProps(leftLane)} />
          <LaneColumn {...columnProps(rightLane)} />
        </Box>
      </Stack>

      <Dialog
        open={departureEditTarget != null}
        onClose={() => {
          setDepartureEditError(null);
          setDepartureEditTarget(null);
        }}
        maxWidth="xs"
        fullWidth
      >
        <DialogTitle>{t("departureDialog_title")}</DialogTitle>
        <DialogContent>
          <TextField
            margin="dense"
            fullWidth
            autoFocus
            type="time"
            label={t("seq_edit_departureLabel")}
            value={departureEditTarget?.draft ?? ""}
            error={departureEditError != null}
            helperText={departureEditError ?? undefined}
            onChange={(e) => {
              setDepartureEditError(null);
              setDepartureEditTarget((prev) =>
                prev ? { ...prev, draft: e.target.value } : prev,
              );
            }}
            onKeyDown={(e) => {
              if (e.key === "Enter" && departureEditTarget) {
                e.preventDefault();
                applyDepartureEdit();
              }
            }}
            InputLabelProps={{ shrink: true }}
            sx={{ mt: 1 }}
          />
          <Typography
            variant="caption"
            color="text.secondary"
            sx={{ mt: 1, display: "block" }}
          >
            {t("departureDialog_hint")}
          </Typography>
        </DialogContent>
        <DialogActions>
          <Button
            onClick={() => {
              setDepartureEditError(null);
              setDepartureEditTarget(null);
            }}
          >
            {t("cancel")}
          </Button>
          <Button variant="contained" onClick={applyDepartureEdit}>
            {t("btn_apply")}
          </Button>
        </DialogActions>
      </Dialog>

      <Dialog
        open={seqEditTarget != null}
        onClose={handleCancelSeqEdit}
        maxWidth="xs"
        fullWidth
      >
        <DialogTitle>{t("seqDialog_title")}</DialogTitle>
        <DialogContent>
          <TextField
            margin="dense"
            fullWidth
            autoFocus
            type="number"
            label={t("seq_edit_seqLabel")}
            value={seqEditTarget?.draft ?? ""}
            onChange={(e) =>
              setSeqEditTarget((prev) =>
                prev ? { ...prev, draft: e.target.value } : prev,
              )
            }
            onKeyDown={(e) => {
              if (e.key === "Enter") {
                e.preventDefault();
                handleCommitSeqEdit();
              }
            }}
            inputProps={{ step: 1, min: 0 }}
            sx={{ mt: 1 }}
          />
          <Typography
            variant="caption"
            color="text.secondary"
            sx={{ mt: 1, display: "block" }}
          >
            {t("schedule_seq_dialog_hint")}
          </Typography>
        </DialogContent>
        <DialogActions>
          <Button onClick={handleCancelSeqEdit}>{t("cancel")}</Button>
          <Button variant="contained" onClick={handleCommitSeqEdit}>
            {t("filter_apply")}
          </Button>
        </DialogActions>
      </Dialog>

      <Dialog
        open={districtAddLaneId != null}
        onClose={closeDistrictAdd}
        maxWidth="xs"
        fullWidth
      >
        <DialogTitle>{t("district_dialog_add")}</DialogTitle>
        <DialogContent>
          <TextField
            margin="dense"
            fullWidth
            autoFocus
            label={t("district_dialog_add")}
            value={districtAddDraft}
            onChange={(e) => {
              setDistrictAddDraft(e.target.value);
              setDistrictAddError(null);
            }}
            error={Boolean(districtAddError)}
            helperText={districtAddError ?? undefined}
            onKeyDown={(e) => {
              if (e.key === "Enter") {
                e.preventDefault();
                applyDistrictAdd();
              }
            }}
            sx={{ mt: 1 }}
          />
        </DialogContent>
        <DialogActions>
          <Button onClick={closeDistrictAdd}>{t("cancel")}</Button>
          <Button variant="contained" onClick={applyDistrictAdd}>
            {t("filter_apply")}
          </Button>
        </DialogActions>
      </Dialog>
    </>
  );
};

export default memo(ScheduleDragWorkspacePane);
