import type {
  AddressLookupInput,
  MothershipAddressCandidate,
} from "@/modules/address/types";
import { AxelHttpClient } from "@/lib/axel/client";
import { getAxelCandidateVerifyTopK } from "@/lib/axel/constants";
import type { AxelSearchCandidate } from "@/lib/axel/pick-candidate";
import {
  expandDirectionalStreet,
  sortMothershipCandidatesForDraft,
} from "@/lib/address/street-normalize";
import { labelToCandidate } from "@/workers/rpa/address-suggestions";

const UNAVAILABLE_REASON =
  "MotherShip 无法定位此地址，请选择其他候选或修改街道信息";

function buildSearchQuery(address: AddressLookupInput): string {
  return [address.street, address.city, address.state, address.zip]
    .map((part) => part?.trim())
    .filter(Boolean)
    .join(", ");
}

function parseStateFromSecondary(secondary: string): {
  city: string;
  state: string;
  zip: string;
} {
  const m = secondary.match(/^([^,]+),\s*([A-Z]{2})(?:\s+(\d{5}(?:-\d{4})?))?/);
  if (m) {
    return { city: m[1]!.trim(), state: m[2]!, zip: m[3] ?? "" };
  }
  const parts = secondary.split(",").map((p) => p.trim());
  return {
    city: parts[0] ?? "",
    state: parts[1]?.slice(0, 2) ?? "",
    zip: "",
  };
}

/** axel search 行 → 候选；展示文案与 API description 一致 */
export function axelSearchRowToCandidate(
  row: AxelSearchCandidate,
  query: AddressLookupInput,
): MothershipAddressCandidate | null {
  const description = row.description.trim();
  const optionId = row.placeId.trim();
  if (!description || !optionId) {
    return null;
  }

  const parsed = labelToCandidate(description, query);
  if (parsed) {
    return {
      ...parsed,
      option_id: optionId,
      display_label: description,
      formatted_address: description,
    };
  }

  const mainText = (row.mainText ?? description.split(",")[0] ?? "").trim();
  const secondary = (row.secondaryText ?? "").trim();
  const tail = parseStateFromSecondary(secondary);
  if (!mainText) {
    return null;
  }

  return {
    option_id: optionId,
    display_label: description,
    formatted_address: description,
    street: mainText,
    city: tail.city || query.city,
    state: tail.state || query.state,
    zip: tail.zip || query.zip,
  };
}

function sortCandidatesForQuery(
  candidates: MothershipAddressCandidate[],
  query: AddressLookupInput,
): MothershipAddressCandidate[] {
  return sortMothershipCandidatesForDraft(candidates, {
    street: query.street,
    city: query.city,
    state: query.state,
  });
}

async function searchSideCandidates(
  client: AxelHttpClient,
  query: AddressLookupInput,
): Promise<MothershipAddressCandidate[]> {
  const queries = [buildSearchQuery(query)];
  const expandedStreet = expandDirectionalStreet(query.street);
  if (expandedStreet && expandedStreet.toLowerCase() !== query.street.trim().toLowerCase()) {
    queries.push(
      buildSearchQuery({
        ...query,
        street: expandedStreet,
      }),
    );
  }

  const rows: AxelSearchCandidate[] = [];
  for (const searchQuery of queries) {
    const batch = await client.searchLocation(searchQuery);
    rows.push(...batch);
  }

  return dedupeByOptionId(
    rows
      .map((row) => axelSearchRowToCandidate(row, query))
      .filter((row): row is MothershipAddressCandidate => row != null),
  );
}

function dedupeByOptionId(
  candidates: MothershipAddressCandidate[],
): MothershipAddressCandidate[] {
  const seen = new Set<string>();
  const out: MothershipAddressCandidate[] = [];
  for (const row of candidates) {
    if (seen.has(row.option_id)) {
      continue;
    }
    seen.add(row.option_id);
    out.push(row);
  }
  return out;
}

async function verifyCandidate(
  client: AxelHttpClient,
  candidate: MothershipAddressCandidate,
): Promise<MothershipAddressCandidate> {
  try {
    await client.fetchPlace(candidate.option_id);
    return { ...candidate, selectable: true };
  } catch {
    return {
      ...candidate,
      selectable: false,
      unavailable_reason: UNAVAILABLE_REASON,
    };
  }
}

async function verifyCandidates(
  client: AxelHttpClient,
  candidates: MothershipAddressCandidate[],
): Promise<MothershipAddressCandidate[]> {
  if (candidates.length === 0) {
    return [];
  }

  const topK = getAxelCandidateVerifyTopK();
  const toVerify = candidates.slice(0, topK);
  const rest = candidates.slice(topK);

  const verified = await Promise.all(
    toVerify.map((c) => verifyCandidate(client, c)),
  );
  const unverified = rest.map((c) => ({ ...c, selectable: true as const }));
  return [...verified, ...unverified];
}

function assertHasSelectable(
  candidates: MothershipAddressCandidate[],
  sideLabel: string,
): void {
  if (candidates.some((c) => c.selectable !== false)) {
    return;
  }
  throw new Error(
    `${sideLabel}地址在 MotherShip 均无法定位，请修改街道或城市后重试`,
  );
}

export async function resolveAxelMothershipCandidates(input: {
  pickup: AddressLookupInput;
  delivery: AddressLookupInput;
}): Promise<{
  pickup_candidates: MothershipAddressCandidate[];
  delivery_candidates: MothershipAddressCandidate[];
}> {
  const client = await AxelHttpClient.create();
  const [pickupRaw, deliveryRaw] = await Promise.all([
    searchSideCandidates(client, input.pickup),
    searchSideCandidates(client, input.delivery),
  ]);

  const pickup_candidates = await verifyCandidates(
    client,
    sortCandidatesForQuery(pickupRaw, input.pickup),
  );
  const delivery_candidates = await verifyCandidates(
    client,
    sortCandidatesForQuery(deliveryRaw, input.delivery),
  );

  if (pickup_candidates.length === 0) {
    throw new Error("MotherShip 未返回提货地址候选");
  }
  if (delivery_candidates.length === 0) {
    throw new Error("MotherShip 未返回派送地址候选");
  }

  assertHasSelectable(pickup_candidates, "提货");
  assertHasSelectable(delivery_candidates, "派送");

  return { pickup_candidates, delivery_candidates };
}

/**
 * 登录后一级表单：单 query 联想（键入下拉）。
 * 仅透传 MotherShip Axel `location/search` 的 googlePlacesResults（含 placeId），
 * 禁止本系统伪造候选；并对 TopK 做 fetchPlace 校验，不可定位则 selectable=false。
 */
export async function resolveAxelMothershipSuggest(
  query: string,
): Promise<MothershipAddressCandidate[]> {
  const trimmed = query.trim();
  if (trimmed.length < 3) {
    return [];
  }

  const lookup: AddressLookupInput = {
    street: trimmed,
    city: "",
    state: "",
    zip: "",
  };
  const client = await AxelHttpClient.create();
  const rows = await client.searchLocation(trimmed);
  const mapped = dedupeByOptionId(
    rows
      .map((row) => axelSearchRowToCandidate(row, lookup))
      .filter((row): row is MothershipAddressCandidate => row != null)
      .filter((c) => c.option_id.trim().length > 0),
  );
  if (mapped.length === 0) {
    return [];
  }
  return verifyCandidates(
    client,
    sortCandidatesForQuery(mapped, lookup),
  );
}
