#!/usr/bin/env tsx
/**
 * 人工 docx 10 组 — 系统全链路探针（HTTP API，非 RPA 代码直调）
 *
 * 流程：候选 API → POST /api/quotes → 轮询 GET /api/quotes/:id → 校验 done + tiers
 * 地址来源：docs/美国跨州货物运输报价汇总_edited3.docx
 *
 * 前置：npm run dev:start（Next + RPA Worker + Redis + MySQL）
 *
 * PowerShell:
 *   npm run probe:human-doc-system
 *   $env:PROBE_HUMAN_ONLY="H01,H07"; npm run probe:human-doc-system
 */
import { randomUUID } from "node:crypto";
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { loadDevEnv } from "@/lib/dev-env";
import { ensureNativeInfraIfNeeded } from "@/lib/infra/ensure-native-infra";
import {
  hostCreateQuote,
  hostFetchMothershipCandidates,
  hostGetQuote,
} from "@/lib/frontend/api-client";
import { applyMothershipCandidate } from "@/lib/frontend/mothership-address";
import type { MothershipAddressCandidate, QuoteRequestBody } from "@/lib/frontend/types";
import { pollQuoteUntilDone } from "@/hooks/use-quote-polling";
import { isAnyRpaWorkerHealthy } from "@/lib/rpa/worker-health";
import { ensureRedisReady, resetRedisClient } from "@/lib/redis";
import {
  HUMAN_DOC_10_PAIRS,
  HUMAN_DOC_CARGO,
  type HumanDocPair,
} from "./lib/human-doc-10pairs";
import { pickBestMothershipCandidate } from "./lib/pick-mothership-candidate";

loadDevEnv();

const BASE = process.env.PROBE_BASE_URL ?? "http://localhost:3000";
const TOKEN = "demo-host-token";
const CUSTOMER_ID = "CUST_001";
const POLL_TIMEOUT_MS = Number(
  process.env.PROBE_HUMAN_POLL_MS ?? process.env.QUOTE_TIMEOUT_MS ?? 600_000,
);
const ONLY = (process.env.PROBE_HUMAN_ONLY ?? "")
  .split(",")
  .map((s) => s.trim().toUpperCase())
  .filter(Boolean);

function selectPairs(): HumanDocPair[] {
  if (!ONLY.length) {
    return HUMAN_DOC_10_PAIRS;
  }
  return HUMAN_DOC_10_PAIRS.filter((p) => ONLY.includes(p.id));
}

function uniqueWeight(base: number): number {
  const nonce = (parseInt(randomUUID().replace(/-/g, ""), 16) % 900) + 1;
  return base + nonce;
}

async function waitForHttpOk(url: string, attempts = 60): Promise<void> {
  for (let i = 0; i < attempts; i += 1) {
    try {
      const res = await fetch(url);
      if (res.ok) {
        return;
      }
    } catch {
      /* retry */
    }
    await new Promise((r) => setTimeout(r, 2000));
  }
  throw new Error(`服务未就绪: ${url}（请先 npm run dev:start）`);
}

function draftAddress(
  draft: { street: string; city: string; state: string },
): QuoteRequestBody["pickup_address"] {
  const street = draft.street.trim();
  const city = draft.city.trim();
  const state = draft.state.trim();
  return {
    street,
    city,
    state,
    zip: "",
    place_id: `draft_${street}_${city}_${state}`.replace(/\s+/g, "_").slice(0, 64),
    formatted_address: `${street}, ${city}, ${state}`,
    selected_from_suggestions: true,
  };
}

function fmtMs(ms: number): string {
  if (ms < 60_000) return `${(ms / 1000).toFixed(1)}s`;
  const m = Math.floor(ms / 60_000);
  const s = ((ms % 60_000) / 1000).toFixed(0);
  return `${m}m${s}s`;
}

type CaseResult = {
  id: string;
  route: string;
  ok: boolean;
  quoteId?: string;
  tierCount?: number;
  sourceType?: string;
  durationMs: number;
  pickupLabel?: string;
  deliveryLabel?: string;
  quotes?: Array<{
    serviceLevel: string;
    rateOption: string;
    rawTotal: number;
    carrier?: string;
  }>;
  error?: string;
};

async function runSystemQuote(pair: HumanDocPair): Promise<CaseResult> {
  const started = Date.now();
  const userPickup = { ...pair.pickup, zip: "" };
  const userDelivery = { ...pair.delivery, zip: "" };

  try {
    const cand = await hostFetchMothershipCandidates(
      BASE,
      TOKEN,
      CUSTOMER_ID,
      userPickup,
      userDelivery,
    );
    if (cand.code !== 0 || !cand.data) {
      return {
        id: pair.id,
        route: pair.route,
        ok: false,
        durationMs: Date.now() - started,
        error: cand.message ?? "地址候选 API 失败",
      };
    }

    const selectedPickup = pickBestMothershipCandidate(
      cand.data.pickup_candidates,
      userPickup,
    );
    const selectedDelivery = pickBestMothershipCandidate(
      cand.data.delivery_candidates,
      userDelivery,
    );
    if (!selectedPickup || !selectedDelivery) {
      return {
        id: pair.id,
        route: pair.route,
        ok: false,
        durationMs: Date.now() - started,
        error: "地址候选为空",
      };
    }

    const body: QuoteRequestBody = {
      request_id: randomUUID(),
      customer_id: CUSTOMER_ID,
      quote_session_id: cand.data.quote_session_id,
      pickup_address: applyMothershipCandidate(
        draftAddress(userPickup),
        selectedPickup,
      ),
      delivery_address: applyMothershipCandidate(
        draftAddress(userDelivery),
        selectedDelivery,
      ),
      weight: { value: uniqueWeight(HUMAN_DOC_CARGO.weightLb), unit: "lb" },
      dimensions: {
        length: HUMAN_DOC_CARGO.dimsIn.length,
        width: HUMAN_DOC_CARGO.dimsIn.width,
        height: HUMAN_DOC_CARGO.dimsIn.height,
        unit: "in",
      },
      pallet_count: HUMAN_DOC_CARGO.palletCount,
      cargo_type: HUMAN_DOC_CARGO.cargoType,
    };

    const created = await hostCreateQuote(BASE, TOKEN, body);
    if (created.code !== 0 || !created.data?.quote_id) {
      return {
        id: pair.id,
        route: pair.route,
        ok: false,
        durationMs: Date.now() - started,
        error: created.message ?? "POST /api/quotes 失败",
      };
    }

    const quoteId = created.data.quote_id;
    let finalDetail = created.data;

    if (finalDetail.status === "processing") {
      const poll = await pollQuoteUntilDone(
        async () => {
          const detail = await hostGetQuote(BASE, TOKEN, CUSTOMER_ID, quoteId);
          if (detail.code !== 0) {
            return { ok: false, errorMessage: detail.message };
          }
          return { ok: true, data: detail.data };
        },
        { timeoutMs: POLL_TIMEOUT_MS },
      );
      if (poll.type !== "done") {
        return {
          id: pair.id,
          route: pair.route,
          ok: false,
          quoteId,
          durationMs: Date.now() - started,
          error:
            poll.type === "timeout"
              ? `轮询超时（${fmtMs(POLL_TIMEOUT_MS)}）`
              : poll.message,
        };
      }
      finalDetail = poll.quote;
    }

    const tierCount = finalDetail?.quotes?.length ?? 0;
    const sourceType = String(finalDetail?.source_type ?? "");
    const ok =
      finalDetail?.status === "done" &&
      tierCount >= 1 &&
      (sourceType === "rpa" || sourceType === "cache");

    return {
      id: pair.id,
      route: pair.route,
      ok,
      quoteId,
      tierCount,
      sourceType,
      durationMs: Date.now() - started,
      pickupLabel: selectedPickup.display_label,
      deliveryLabel: selectedDelivery.display_label,
      quotes: (finalDetail?.quotes ?? []).map((q) => ({
        serviceLevel: q.service_level,
        rateOption: q.rate_option,
        rawTotal: q.raw_total,
        carrier: q.carrier,
      })),
      error: ok
        ? undefined
        : `status=${finalDetail?.status} tiers=${tierCount} source=${sourceType}`,
    };
  } catch (error) {
    return {
      id: pair.id,
      route: pair.route,
      ok: false,
      durationMs: Date.now() - started,
      error: error instanceof Error ? error.message : String(error),
    };
  }
}

async function main(): Promise<void> {
  const runId = randomUUID().slice(0, 8);
  const pairs = selectPairs();
  if (!pairs.length) {
    console.error(`[probe:human-doc] 无匹配路线 PROBE_HUMAN_ONLY=${ONLY.join(",")}`);
    process.exit(1);
  }

  await ensureNativeInfraIfNeeded();
  resetRedisClient();
  await ensureRedisReady();
  await waitForHttpOk(`${BASE}/`);

  const workerOk = await isAnyRpaWorkerHealthy();
  console.log(
    `[probe:human-doc] 系统全链路探针 run=${runId} pairs=${pairs.length} worker=${workerOk ? "ok" : "未就绪"}`,
  );
  console.log(`[probe:human-doc] API=${BASE} 策略=direct-first-widget-fallback`);

  const results: CaseResult[] = [];
  let pass = 0;

  for (const pair of pairs) {
    console.log(`\n[probe:human-doc] ${pair.id} ${pair.route}`);
    const outcome = await runSystemQuote(pair);
    results.push(outcome);
    if (outcome.ok) {
      pass += 1;
      const lowest = outcome.quotes?.find(
        (q) => q.serviceLevel === "standard" && q.rateOption === "lowest",
      );
      console.log(
        `[PASS] quote=${outcome.quoteId} tiers=${outcome.tierCount} source=${outcome.sourceType} ${fmtMs(outcome.durationMs)} lowest=${lowest?.rawTotal ?? "—"}`,
      );
    } else {
      console.log(`[FAIL] ${outcome.error ?? "unknown"} (${fmtMs(outcome.durationMs)})`);
    }
  }

  const report = {
    runId,
    mode: "system-api",
    sourceDoc: "docs/美国跨州货物运输报价汇总_edited3.docx",
    finishedAt: new Date().toISOString(),
    baseUrl: BASE,
    summary: { total: pairs.length, pass, fail: pairs.length - pass },
    verdict: pass === pairs.length ? "PASS" : "FAIL",
    env: {
      quoteStrategy: "direct-first-widget-fallback",
      RPA_ADDRESS_MODE: process.env.RPA_ADDRESS_MODE ?? "",
      workerHealthy: workerOk,
    },
    results,
  };

  const outDir = join(process.cwd(), ".rpa", "human-doc-system-probe");
  if (!existsSync(outDir)) {
    mkdirSync(outDir, { recursive: true });
  }
  const outPath = join(outDir, `system-${runId}.json`);
  writeFileSync(outPath, JSON.stringify(report, null, 2), "utf8");

  console.log(`\n[probe:human-doc] ${pass}/${pairs.length} 通过 | 报告: ${outPath}`);
  process.exit(pass === pairs.length ? 0 : 1);
}

main().catch((e) => {
  console.error(e);
  process.exit(1);
});
