import type { RouteLaneImportPreviewRow } from "@/app/api/shop/client";
import type { Truck } from "@/app/api/shop/actions";
import { normalizeStoreId } from "@/app/utils/formatUtil";

export type ImportPreviewLane = {
  id: string;
  truckLanceCode: string;
  plate?: string;
  logisticsCompany?: string;
  logisticId?: number | null;
  driver?: string;
  phone?: string;
  startTime: string;
  storeId: string;
  remark?: string | null;
  shops: ImportPreviewShopCard[];
};

export type ImportPreviewShopCard = {
  id: number;
  shopEntityId?: number | null;
  branchName: string;
  shopCode: string;
  districtReferenceRaw: string | null;
  loadingSequence: number;
  remark?: string | null;
  storeId: string;
  departureTime: string;
};

const LANE_KEY_SEP = "|";

export function encodeLaneIdForImport(
  truckLanceCode: string,
  remark: string | null | undefined,
): string {
  const code = String(truckLanceCode || "").trim();
  const rem =
    remark != null && String(remark).trim() !== ""
      ? String(remark).trim()
      : "";
  return `${encodeURIComponent(code)}${LANE_KEY_SEP}${encodeURIComponent(rem)}`;
}

function parseTimeForPreview(raw: string): string {
  const s = String(raw ?? "").trim();
  if (!s) return "00:00:00";
  const m = s.match(/^(\d{1,2}):(\d{2})(?::(\d{2}))?/);
  if (!m) return s;
  const h = String(Math.min(23, Math.max(0, Number(m[1]) || 0))).padStart(2, "0");
  const min = String(Math.min(59, Math.max(0, Number(m[2]) || 0))).padStart(2, "0");
  const sec = m[3] != null ? String(Math.min(59, Math.max(0, Number(m[3]) || 0))).padStart(2, "0") : "00";
  return `${h}:${min}:${sec}`;
}

function shopCardFromPreview(
  row: RouteLaneImportPreviewRow,
  id: number,
): ImportPreviewShopCard {
  return {
    id,
    shopEntityId: row.shopId,
    branchName: String(row.shopName || "").trim(),
    shopCode: String(row.shopCode || "").trim(),
    districtReferenceRaw:
      row.districtReference != null &&
      String(row.districtReference).trim() !== ""
        ? String(row.districtReference).trim()
        : null,
    loadingSequence: Number(row.loadingSequence ?? 0) || 0,
    remark: row.remark != null ? String(row.remark) : null,
    storeId: normalizeStoreId(row.storeId),
    departureTime: parseTimeForPreview(row.departureTime),
  };
}

function previewRowToTruck(row: RouteLaneImportPreviewRow, id: number): Truck {
  return {
    id,
    truckLanceCode: row.truckLanceCode,
    departureTime: row.departureTime,
    loadingSequence: row.loadingSequence,
    districtReference: row.districtReference,
    storeId: row.storeId,
    remark: row.remark,
    shopName: row.shopName,
    shopCode: row.shopCode,
    logisticId: row.logisticId,
    shop: { id: row.shopId },
  } as Truck;
}

/** Merge parsed import rows into current lanes (upsert only; does not remove shops omitted from file). */
export function mergeImportPreviewIntoLanes<
  T extends ImportPreviewLane,
>(current: T[], rows: RouteLaneImportPreviewRow[]): T[] {
  let nextTempId = -1;
  const laneMap = new Map<string, T>(
    current.map((l) => [l.id, { ...l, shops: [...l.shops] } as T]),
  );

  const byLane = new Map<string, RouteLaneImportPreviewRow[]>();
  for (const row of rows) {
    const lid = encodeLaneIdForImport(row.truckLanceCode, row.remark);
    const arr = byLane.get(lid) ?? [];
    arr.push(row);
    byLane.set(lid, arr);
  }

  for (const [laneId, previewRows] of Array.from(byLane.entries())) {
    previewRows.sort(
      (a: RouteLaneImportPreviewRow, b: RouteLaneImportPreviewRow) =>
        (a.loadingSequence ?? 0) - (b.loadingSequence ?? 0),
    );
    const code = String(previewRows[0]?.truckLanceCode ?? "").trim();
    const remark =
      previewRows[0]?.remark != null &&
      String(previewRows[0].remark).trim() !== ""
        ? String(previewRows[0].remark).trim()
        : null;
    const storeId = normalizeStoreId(previewRows[0]?.storeId ?? "2F");
    const startTime = parseTimeForPreview(previewRows[0]?.departureTime ?? "");

    const existing = laneMap.get(laneId);
    if (existing) {
      const shops = [...existing.shops];
      for (const pr of previewRows) {
        const sid =
          pr.truckRowId != null && Number.isFinite(pr.truckRowId) && pr.truckRowId > 0
            ? pr.truckRowId
            : nextTempId--;
        const card = shopCardFromPreview(pr, sid);
        const idx = shops.findIndex(
          (s) =>
            (pr.truckRowId != null && pr.truckRowId > 0 && s.id === pr.truckRowId) ||
            String(s.shopCode).trim().toLowerCase() ===
              String(pr.shopCode).trim().toLowerCase(),
        );
        if (idx >= 0) {
          shops[idx] = { ...shops[idx], ...card, id: shops[idx]!.id };
        } else {
          shops.push(card as T["shops"][number]);
        }
      }
      shops.sort((a, b) => a.loadingSequence - b.loadingSequence);
      laneMap.set(laneId, {
        ...existing,
        startTime,
        storeId,
        shops,
      } as T);
    } else {
      const trucks: Truck[] = previewRows.map((pr: RouteLaneImportPreviewRow) => {
        const sid =
          pr.truckRowId != null && Number.isFinite(pr.truckRowId) && pr.truckRowId > 0
            ? pr.truckRowId
            : nextTempId--;
        return previewRowToTruck(pr, sid);
      });
      const shops = trucks.map((t, i) =>
        shopCardFromPreview(previewRows[i]!, Number(t.id)),
      );
      const logisticId =
        previewRows.find((r: RouteLaneImportPreviewRow) => r.logisticId != null)
          ?.logisticId ?? null;
      laneMap.set(laneId, {
        id: laneId,
        truckLanceCode: code,
        remark,
        storeId,
        startTime,
        logisticId,
        logisticsCompany: "",
        plate: "",
        driver: "",
        phone: "",
        shops: shops as T["shops"],
      } as T);
    }
  }

  const merged = Array.from(laneMap.values());
  merged.sort((a, b) => {
    const c = a.truckLanceCode.localeCompare(b.truckLanceCode, "zh-Hant");
    if (c !== 0) return c;
    return String(a.remark ?? "").localeCompare(
      String(b.remark ?? ""),
      "zh-Hant",
    );
  });
  return merged;
}
