import type { QuoteDetail } from "@/lib/frontend/types";
import {
  POLL_INTERVAL_MS,
  POLL_TIMEOUT_MS,
} from "@/lib/frontend/constants";
import { formatQuoteErrorMessage } from "@/modules/quote/quote-error-messages";

export type PollFetchQuote = () => Promise<{
  ok: boolean;
  data?: QuoteDetail;
  errorMessage?: string;
}>;

export type PollResult =
  | { type: "done"; quote: QuoteDetail }
  | { type: "timeout" }
  | { type: "error"; message: string; quote?: QuoteDetail };

const BASE_BACKOFF_MS = 500;
/** 轮询超过该时长后缩短间隔，加快感知 RPA 完成 */
const POLL_FAST_AFTER_MS = 10_000;
const POLL_FAST_INTERVAL_MS = 800;

function sleep(ms: number): Promise<void> {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

/** 按 wall-clock 轮询至 timeoutMs（默认与 POLL_TIMEOUT_MS 一致） */
export async function pollQuoteUntilDone(
  fetchQuote: PollFetchQuote,
  options?: {
    timeoutMs?: number;
    fastAfterMs?: number;
    fastIntervalMs?: number;
    /** processing 时每次轮询回调（用于实时进度） */
    onProcessing?: (quote: QuoteDetail) => void;
  },
): Promise<PollResult> {
  let networkFailures = 0;
  const startedAt = Date.now();
  const timeoutMs = options?.timeoutMs ?? POLL_TIMEOUT_MS;
  const fastAfterMs = options?.fastAfterMs ?? POLL_FAST_AFTER_MS;
  const fastIntervalMs = options?.fastIntervalMs ?? POLL_FAST_INTERVAL_MS;
  const deadline = Date.now() + timeoutMs;

  function pollIntervalMs(elapsedMs: number): number {
    if (elapsedMs >= fastAfterMs) {
      return Math.min(fastIntervalMs, POLL_INTERVAL_MS);
    }
    return POLL_INTERVAL_MS;
  }

  while (Date.now() < deadline) {
    const result = await fetchQuote();

    if (!result.ok) {
      if (result.errorMessage?.includes("询价结果接口不可用")) {
        return { type: "error", message: result.errorMessage };
      }
      networkFailures += 1;
      const backoff = Math.min(
        BASE_BACKOFF_MS * 2 ** (networkFailures - 1),
        POLL_INTERVAL_MS,
      );
      const remaining = deadline - Date.now();
      if (remaining <= 0) {
        break;
      }
      await sleep(Math.min(backoff, remaining));
      continue;
    }

    networkFailures = 0;
    const quote = result.data!;

    if (quote.status === "processing") {
      options?.onProcessing?.(quote);
      const remaining = deadline - Date.now();
      if (remaining <= 0) {
        break;
      }
      const interval = pollIntervalMs(Date.now() - startedAt);
      await sleep(Math.min(interval, remaining));
      continue;
    }

    if (quote.status === "failed") {
      return {
        type: "error",
        message: formatQuoteErrorMessage(
          quote.error_code,
          quote.error_message,
        ),
        quote,
      };
    }

    return { type: "done", quote };
  }

  return { type: "timeout" };
}
