import type { QuoteRequest } from "@/modules/providers/quote-provider";
import { pageLocator } from "@/lib/rpa/locator";
import { isRpaAddressMockMode } from "@/lib/rpa/address-mode";
import {
  isRpaAddressRushMode,
  isDualAddressPatchEnabled,
  isBlindAddressFlowActive,
  isPreconfirmedMothershipQuote,
  isRpaBlindAddressFlow,
  resolveBlindAddressFlow,
} from "@/lib/rpa/env";
import { isAddressChipVisuallyCommitted } from "@/workers/rpa/address-chip-visual";
import { RpaError } from "@/modules/rpa/errors";
import { assertRPAContext } from "@/workers/rpa/kernel/context-validator";
import type { RPAContext } from "@/workers/rpa/kernel/context";
import {
  createAddressAdapter,
  formatDiagnosticsFailure,
} from "@/workers/rpa/address-adapter";
import {
  isAddressSuggestionDropdownOpen,
  getAddressConfirmationSurfaceText,
} from "@/workers/rpa/address-suggestions";
import { openAddressSide } from "@/workers/rpa/address-side";
import {
  assertDualAddressStable,
  blurAddressEditing,
  dismissAddressSuggestionDropdown,
  ensureDualAddressCommitted,
  isAddressesVisuallyCompleteForCargo,
  isPickupReadyForDeliveryInput,
  lockPickupIntoChip,
  releasePickupFieldForDelivery,
  settlePickupBeforeDelivery,
  widgetContainsConfirmedAddress,
} from "@/workers/rpa/address-commit";
import { runKernelSubStep } from "@/workers/rpa/kernel/step-guard";
import { stabilizeQuoteLandingPage } from "@/workers/rpa/page-prep";
import {
  assertBothAddressesLocked,
  settleAddressFieldsForCargo,
} from "@/workers/rpa/cargo-lock";
import {
  applyDeliveryDualAddressPatch,
  applyPickupDualAddressPatch,
  preservePickupInWidget,
  releasePickupInputInWidget,
} from "@/workers/rpa/inject/dual-address-patch";

export { widgetContainsConfirmedAddress } from "@/workers/rpa/address-commit";

type AddressFields = QuoteRequest["pickup"];

async function assertConfirmedAddressesInWidget(
  ctx: RPAContext,
  widgetSelector: string,
  pickup: AddressFields,
  delivery: AddressFields,
): Promise<void> {
  const surfaceText = await getAddressConfirmationSurfaceText(
    ctx.page,
    widgetSelector,
  );
  const fullText = await pageLocator(ctx.page, widgetSelector).innerText();
  
  const widgetText = [surfaceText, fullText]
    .map((part) => part.replace(/\s+/g, " ").trim())
    .filter(Boolean)
    .join(" ");
  
  // 快速通道：如果widget已经包含两个地址，信任视觉状态，忽略dropdown检测
  const pickupPresent = widgetContainsConfirmedAddress(widgetText, pickup);
  const deliveryPresent = widgetContainsConfirmedAddress(widgetText, delivery);
  
  if (pickupPresent && deliveryPresent) {
    console.log("[rpa] assertConfirmedAddressesInWidget: 视觉快速通道 - 两地址已在widget中");
    return;
  }
  
  if (
    !surfaceText.trim() &&
    (await isAddressSuggestionDropdownOpen(ctx.page))
  ) {
    throw new RpaError(
      "ADDRESS_NOT_CONFIRMED",
      "地址联想下拉仍打开，未完成点选锁定",
      { retryable: true },
    );
  }

  if (!pickupPresent) {
    throw new RpaError(
      "ADDRESS_NOT_CONFIRMED",
      `widget 未体现已确认提货地址：${pickup.mothershipDisplayLabel}`,
      { retryable: true },
    );
  }
  if (!deliveryPresent) {
    throw new RpaError(
      "ADDRESS_NOT_CONFIRMED",
      `widget 未体现已确认派送地址：${delivery.mothershipDisplayLabel}`,
      { retryable: true },
    );
  }
}

/** 单侧填址 + commit 屏障（pickup 先锁定再开 delivery）；业务流程仅调用 AddressAdapter */
export async function fillAddressWithCommitStep(
  ctx: RPAContext,
  side: "pickup" | "delivery",
  address: AddressFields,
  pickupAnchor?: AddressFields,
): Promise<void> {
  const widgetSelector = ctx.selectors.resolve("RPA_SELECTOR_QUOTE_WIDGET");
  const mockMode = isRpaAddressMockMode();
  const rush = isRpaAddressRushMode();
  const blind = isBlindAddressFlowActive(ctx);
  const rushOrBlind = rush || blind;

  if (side === "delivery") {
    if (!pickupAnchor) {
      throw new RpaError(
        "ADDRESS_NOT_CONFIRMED",
        "填写派送地址前缺少提货锚点地址",
        { retryable: false },
      );
    }
    const pickupVaultReady =
      isDualAddressPatchEnabled() &&
      Boolean(ctx.state.axelPlaceLocations.pickup);
    if (!rushOrBlind) {
      if (pickupVaultReady) {
        console.log(
          "[rpa] address-step: vault pickup 已注入，打开派送侧前 settle",
        );
      }
      await runKernelSubStep("fillAddress", "settlePickupBeforeDelivery", () =>
        settlePickupBeforeDelivery(ctx, widgetSelector, pickupAnchor),
      );
      if (!pickupVaultReady) {
        await runKernelSubStep(
          "fillAddress",
          "assertPickupStableBeforeDelivery",
          () =>
            assertDualAddressStable(ctx, widgetSelector, pickupAnchor, null),
        );
      }
    } else {
      console.log(
        blind
          ? "[rpa] blind-flow: 跳过 settlePickupBeforeDelivery"
          : "[rpa] visual-rush: 跳过 settlePickupBeforeDelivery",
      );
    }
  }

  ctx.state.markAddressSide(side);
  const street = await runKernelSubStep(
    "fillAddress",
    `${side}/openAddressSide`,
    () => openAddressSide(ctx, side),
  );

  if (side === "delivery" && pickupAnchor && !mockMode && !rushOrBlind) {
    const widgetText = await pageLocator(ctx.page, widgetSelector)
      .innerText()
      .catch(() => "");
    if (!widgetContainsConfirmedAddress(widgetText, pickupAnchor)) {
      throw new RpaError(
        "ADDRESS_NOT_CONFIRMED",
        "打开派送侧后提货地址已从 widget 消失",
        { retryable: true },
      );
    }
  }

  if (!(await street.isEditable().catch(() => false))) {
    throw new RpaError(
      "STRUCT_CHANGE",
      `${side === "pickup" ? "提货" : "派送"}地址输入框不可编辑`,
      { retryable: false },
    );
  }

  await runKernelSubStep("fillAddress", `${side}/addressAdapter`, async () => {
    const adapter = createAddressAdapter();

    let commit;
    try {
      await adapter.selectAddress({
        ctx,
        side,
        address,
        street,
        widgetSelector,
        pickupAnchor: side === "delivery" ? pickupAnchor : undefined,
      });
      if (!blind) {
        commit = await adapter.waitForCommit();
      }
    } catch (error) {
      if (blind) {
        console.log(`[rpa] blind-flow: ${side} adapter 异常已忽略，继续下一步`);
        return;
      }
      const chipOk = await isAddressChipVisuallyCommitted(
        ctx.page,
        widgetSelector,
        address,
      );
      if (
        chipOk &&
        (rush ||
          (side === "delivery" &&
            !mockMode &&
            pickupAnchor &&
            (await isAddressesVisuallyCompleteForCargo(
              ctx.page,
              widgetSelector,
              pickupAnchor,
              address,
            ))))
      ) {
        if (rush) {
          console.log(
            `[rpa] visual-rush: ${side} chip 视觉已确认，继续下一步`,
          );
        } else {
          console.log(
            "[rpa] address-step: delivery commit 未通过但视觉双地址已齐，继续货物步",
          );
        }
        if (!rush) {
          await dismissAddressSuggestionDropdown(ctx.page, street);
          await blurAddressEditing(ctx, widgetSelector);
        }
        return;
      }
      if (error instanceof RpaError) {
        const diag = adapter.getDiagnostics();
        if (!error.message.includes("commitState=")) {
          throw new RpaError(
            "ADDRESS_NOT_CONFIRMED",
            formatDiagnosticsFailure(diag),
            { retryable: error.retryable },
          );
        }
      }
      throw error;
    }

    if (blind) {
      console.log(`[rpa] blind-flow: ${side} 完成，不校验 commit`);
      return;
    }

    if (!commit?.committed || adapter.getCommitState() !== "COMMITTED") {
      throw new RpaError(
        "ADDRESS_NOT_CONFIRMED",
        formatDiagnosticsFailure(adapter.getDiagnostics()),
        { retryable: true },
      );
    }

    const diag = adapter.getDiagnostics();
    if (!mockMode && !rush && !diag.placeRequestObserved) {
      throw new RpaError(
        "ADDRESS_NOT_CONFIRMED",
        formatDiagnosticsFailure({
          ...diag,
          status: "no_place_request",
        }),
        { retryable: true },
      );
    }

    if (side === "pickup" && !mockMode && !rush) {
      await lockPickupIntoChip(ctx, widgetSelector, street, address);
      await ctx.page.waitForTimeout(1000);
      let locked = await isPickupReadyForDeliveryInput(
        ctx.page,
        widgetSelector,
        address,
      );
      if (!locked) {
        locked = await releasePickupFieldForDelivery(
          ctx,
          widgetSelector,
          address,
          street,
        );
      }
      if (!locked && diag.placeRequestObserved) {
        console.log(
          "[rpa] address-step: pickup place 200 已观测，chip 未就绪仍继续",
        );
      } else if (!locked) {
        console.log("[rpa] pickup-commit-diagnostic", {
          phase: "after-pickup-adapter",
          placeId: diag.placeId,
          commitState: diag.commitState,
          placeRequestObserved: diag.placeRequestObserved,
          method: diag.method,
        });
        throw new RpaError(
          "ADDRESS_NOT_CONFIRMED",
          "Pickup commit 后 widget 未锁定（无独立派送框）",
          { retryable: true },
        );
      }
      if (!ctx.state.axelPlaceLocations.pickup) {
        throw new RpaError(
          "ADDRESS_NOT_CONFIRMED",
          "Pickup commit 后 axel place 详情未写入状态",
          { retryable: true },
        );
      }
      console.log("[rpa] address-step:pickup-locked", {
        placeId: diag.placeId.slice(0, 24),
        method: diag.method,
        placeRequestObserved: diag.placeRequestObserved,
      });
      if (isDualAddressPatchEnabled() && ctx.state.axelPlaceLocations.pickup) {
        await applyPickupDualAddressPatch(ctx.page, {
          placeId: diag.placeId,
          location: ctx.state.axelPlaceLocations.pickup,
          description:
            address.mothershipDisplayLabel?.trim() ||
            address.formattedAddress?.trim() ||
            address.street,
        });
      }
    }

    if (side === "pickup" && rush) {
      console.log("[rpa] visual-rush: pickup 已点选联想，跳过 chip 锁定轮询");
    }

    if (side === "delivery" && !mockMode && !rush) {
      if (
        isDualAddressPatchEnabled() &&
        ctx.state.axelPlaceLocations.pickup &&
        ctx.state.axelPlaceLocations.delivery &&
        pickupAnchor
      ) {
        await applyDeliveryDualAddressPatch(
          ctx.page,
          {
            placeId: pickupAnchor.placeId ?? "",
            location: ctx.state.axelPlaceLocations.pickup,
            description:
              pickupAnchor.mothershipDisplayLabel?.trim() ||
              pickupAnchor.formattedAddress?.trim() ||
              pickupAnchor.street,
          },
          {
            placeId: diag.placeId,
            location: ctx.state.axelPlaceLocations.delivery,
            description:
              address.mothershipDisplayLabel?.trim() ||
              address.formattedAddress?.trim() ||
              address.street,
          },
        );
      }
      if (diag.pickupLostAfterDelivery) {
        throw new RpaError(
          "ADDRESS_NOT_CONFIRMED",
          "填派送后提货 place commit 丢失（pickupLostAfterDelivery=true）",
          { retryable: true },
        );
      }
      if (!ctx.state.axelPlaceLocations.pickup || !ctx.state.axelPlaceLocations.delivery) {
        throw new RpaError(
          "ADDRESS_NOT_CONFIRMED",
          "双地址 commit 后 axel place 状态不完整",
          { retryable: true },
        );
      }
      console.log("[rpa] address-step:dual-address-committed", {
        pickupPlaceId: diag.placeId.slice(0, 24),
        pickupLostAfterDelivery: diag.pickupLostAfterDelivery ?? false,
        placeRequestObserved: diag.placeRequestObserved,
      });
    }

    await dismissAddressSuggestionDropdown(ctx.page, street);
    await blurAddressEditing(ctx, widgetSelector);
  });
}

/** Kernel 地址填表步骤（双地址分步 commit，禁止 quote_session 跳过） */
export async function fillAddressStep(
  ctx: RPAContext,
  req: QuoteRequest,
): Promise<void> {
  assertRPAContext(ctx, "fillAddressStep");

  const widgetSelector = ctx.selectors.resolve("RPA_SELECTOR_QUOTE_WIDGET");
  const mockMode = isRpaAddressMockMode();

  if (mockMode) {
    console.log(
      "[rpa] RPA_ADDRESS_MODE=mock：仅替代 MotherShip commit，cargo/submit/quote 仍真实执行",
    );
  }

  ctx.state.blindAddressFlow = resolveBlindAddressFlow(req);
  const rush = isRpaAddressRushMode();
  const blind = isBlindAddressFlowActive(ctx);
  if (blind) {
    console.log(
      isPreconfirmedMothershipQuote(req) && !isRpaBlindAddressFlow()
        ? "[rpa] blind-flow: MotherShip 候选已确认，填表+点选，仅最终验报价"
        : "[rpa] blind-flow: 地址步填表+点选，仅最终验报价",
    );
  } else if (rush) {
    console.log("[rpa] visual-rush: 地址步仅填表+点选，跳过锁定/重填闸门");
  }

  await runKernelSubStep("fillAddress", "stabilizeLandingPage", () =>
    stabilizeQuoteLandingPage(ctx),
  );

  await fillAddressWithCommitStep(ctx, "pickup", req.pickup);
  if (!mockMode && !rush && !blind) {
    const pickupVaultReady =
      isDualAddressPatchEnabled() && Boolean(ctx.state.axelPlaceLocations.pickup);
    if (isDualAddressPatchEnabled()) {
      await preservePickupInWidget(ctx.page);
      await releasePickupInputInWidget(ctx.page);
      await ctx.page.waitForTimeout(600);
    }
    if (!pickupVaultReady) {
      await runKernelSubStep("fillAddress", "assertPickupCommitted", () =>
        assertDualAddressStable(ctx, widgetSelector, req.pickup, null),
      );
    } else {
      console.log(
        "[rpa] address-step: preknown pickup 已注入 vault，跳过 assertPickupCommitted",
      );
    }
  }

  await fillAddressWithCommitStep(
    ctx,
    "delivery",
    req.delivery,
    req.pickup,
  );

  const visualReady =
    !mockMode &&
    (await isAddressesVisuallyCompleteForCargo(
      ctx.page,
      widgetSelector,
      req.pickup,
      req.delivery,
    ));

  if (visualReady) {
    console.log(
      "[rpa] fillAddress: 视觉已确认双地址（widget/地图），直接进入货物步",
    );
  } else if (!mockMode && !rush && !blind) {
    await runKernelSubStep("fillAddress", "ensureDualAddressCommitted", () =>
      ensureDualAddressCommitted(
        ctx,
        widgetSelector,
        req.pickup,
        req.delivery,
        async (refillSide, refillAddress) => {
          await fillAddressWithCommitStep(
            ctx,
            refillSide,
            refillAddress,
            refillSide === "delivery" ? req.pickup : undefined,
          );
        },
      ),
    );
  }

  await ctx.page.keyboard.press("Escape").catch(() => undefined);
  if (!rush) {
    await runKernelSubStep("fillAddress", "settleAddressEditing", () =>
      ctx.state.settleAddressEditing(),
    );
    await runKernelSubStep("fillAddress", "settleAddressFieldsForCargo", () =>
      settleAddressFieldsForCargo(ctx.page, widgetSelector),
    );
  }

  if (!mockMode && !visualReady && !rush && !blind) {
    await runKernelSubStep("fillAddress", "assertConfirmedAddressesInWidget", () =>
      assertConfirmedAddressesInWidget(
        ctx,
        widgetSelector,
        req.pickup,
        req.delivery,
      ),
    );
    await runKernelSubStep("fillAddress", "assertBothAddressesLocked", () =>
      assertBothAddressesLocked(ctx.page, widgetSelector),
    );
  }
}
