import { normalizeStoreId } from "@/app/utils/formatUtil";

/** Shared display-order helpers for RouteBoard and schedule drag workspace. */

export type DisplayOrderShop = {
  id: number;
  districtReferenceRaw?: string | null;
  loadingSequence?: number | null;
};

export function toDistrictDisplayName(
  district: string | null | undefined,
): string {
  const value = String(district ?? "").trim();
  return value === "" ? "未分類" : value;
}

export function toDistrictRawValue(
  district: string | null | undefined,
): string | null {
  const value = String(district ?? "").trim();
  return value === "" || value === "未分類" ? null : value;
}

/** Shop card subtitle on RouteBoard (branch · code · store; district is in section header). */
export function formatShopCardSubtitle(shop: {
  branchName?: string | null;
  shopCode: string;
  storeId?: string | null;
}): string {
  const parts: string[] = [];
  const branch = String(shop.branchName ?? "").trim();
  if (branch !== "") parts.push(branch);
  parts.push(String(shop.shopCode || "").trim() || "-");
  const store = normalizeStoreId(shop.storeId);
  if (store !== "" && store !== "-") parts.push(store);
  return parts.join(" · ");
}

/** Subtitle for schedule review queue; always reflects current districtReferenceRaw. */
export function formatPlannedShopLocation(shop: {
  districtReferenceRaw: string | null;
  shopCode: string;
  storeId?: string | null;
}): string {
  const district = toDistrictDisplayName(shop.districtReferenceRaw);
  const parts = [district, String(shop.shopCode || "").trim() || "-"];
  const store = normalizeStoreId(shop.storeId);
  if (store !== "" && store !== "-") parts.push(store);
  return parts.join(" · ");
}

export function dedupeDistrictPendingOrder(items: string[]): string[] {
  const seen = new Set<string>();
  const out: string[] = [];
  for (const x of items) {
    if (seen.has(x)) continue;
    seen.add(x);
    out.push(x);
  }
  return out;
}

export function districtDisplayExistsInShops<T extends DisplayOrderShop>(
  shops: T[],
  pendingExtra: string[] | undefined,
  display: string,
): boolean {
  const set = new Set(groupByDistrict(shops).map((g) => g.district));
  for (const d of pendingExtra ?? []) set.add(d);
  return set.has(display);
}

export function buildLaneDistrictSections<T extends DisplayOrderShop>(
  shops: T[],
  pendingExtraDistrictDisplays: string[] | undefined,
): Array<{ district: string; shops: T[]; isPendingEmpty: boolean }> {
  const grouped = groupByDistrict(shops);
  const keysFromShops = new Set(grouped.map((g) => g.district));
  const extras = (pendingExtraDistrictDisplays ?? []).filter(
    (d) => !keysFromShops.has(d),
  );
  const merged = [
    ...grouped.map((g) => ({
      district: g.district,
      shops: g.shops,
      isPendingEmpty: false,
    })),
    ...extras.map((district) => ({
      district,
      shops: [] as T[],
      isPendingEmpty: true,
    })),
  ];
  merged.sort((a, b) => {
    if (a.district === "未分類") return -1;
    if (b.district === "未分類") return 1;
    return a.district.localeCompare(b.district, "zh-Hant");
  });
  return merged;
}

export function groupByDistrict<T extends DisplayOrderShop>(
  shops: T[],
): Array<{ district: string; shops: T[] }> {
  const map = new Map<string, T[]>();
  for (const s of shops) {
    const key = toDistrictDisplayName(s.districtReferenceRaw);
    const arr = map.get(key) ?? [];
    arr.push(s);
    map.set(key, arr);
  }
  return Array.from(map.entries())
    .sort(([a], [b]) => {
      if (a === "未分類") return -1;
      if (b === "未分類") return 1;
      return a.localeCompare(b, "zh-Hant");
    })
    .map(([district, list]) => ({
      district,
      shops: list
        .slice()
        .sort(
          (x, y) => (x.loadingSequence ?? 0) - (y.loadingSequence ?? 0),
        ),
    }));
}

export function flattenDisplayOrder<T extends DisplayOrderShop>(
  shops: T[],
): T[] {
  return groupByDistrict(shops).flatMap((g) => g.shops);
}

export function computeMovedLoadingSequence<T extends DisplayOrderShop>(
  inserted: T[],
  movedId: number,
  preferPrevious = false,
): number {
  const idx = inserted.findIndex((s) => s.id === movedId);
  if (idx < 0) return 0;
  if (preferPrevious) {
    const prev = inserted[idx - 1];
    if (prev && typeof prev.loadingSequence === "number")
      return prev.loadingSequence;
  }
  const next = inserted[idx + 1];
  if (next && typeof next.loadingSequence === "number")
    return next.loadingSequence;
  const prev = inserted[idx - 1];
  if (prev && typeof prev.loadingSequence === "number")
    return prev.loadingSequence;
  const max = inserted.reduce(
    (m, s) => Math.max(m, Number(s.loadingSequence ?? 0) || 0),
    0,
  );
  return max;
}
