import { describe, expect, it, vi, beforeEach } from "vitest";

vi.mock("@/modules/address/mothership-candidates", () => ({
  resolveMothershipCandidates: vi.fn(),
}));

vi.mock("@/modules/host/public-session", () => ({
  saveHostPublicSession: vi.fn(),
  loadHostPublicSession: vi.fn(),
  deleteHostPublicSession: vi.fn(),
}));

vi.mock("@/modules/quote/orchestrator", () => ({
  submitQuote: vi.fn(),
}));

vi.mock("@/modules/host/wait-for-quote", () => ({
  waitForQuoteDetail: vi.fn(),
  HostQuoteWaitError: class HostQuoteWaitError extends Error {
    code = "QUOTE_FAILED";
    detail = undefined;
  },
}));

vi.mock("@/lib/prisma", () => ({
  prisma: {
    quoteRecord: {
      findFirst: vi.fn(),
    },
  },
}));

import { resolveMothershipCandidates } from "@/modules/address/mothership-candidates";
import {
  deleteHostPublicSession,
  loadHostPublicSession,
  saveHostPublicSession,
} from "@/modules/host/public-session";
import {
  hostPublicResolveCandidates,
  hostPublicSubmitAndWait,
} from "@/modules/host/public-orchestrator";
import { waitForQuoteDetail } from "@/modules/host/wait-for-quote";
import { submitQuote } from "@/modules/quote/orchestrator";
import { prisma } from "@/lib/prisma";

const pickupCandidate = {
  option_id: "pickup-opt-1",
  display_label: "1234 Warehouse St, Los Angeles, CA, USA",
  formatted_address: "1234 Warehouse St, Los Angeles, CA, USA",
  street: "1234 Warehouse St",
  city: "Los Angeles",
  state: "CA",
  zip: "90001",
  selectable: true,
};

const deliveryCandidate = {
  option_id: "delivery-opt-1",
  display_label: "5678 Distribution Dr, Dallas, TX, USA",
  formatted_address: "5678 Distribution Dr, Dallas, TX, USA",
  street: "5678 Distribution Dr",
  city: "Dallas",
  state: "TX",
  zip: "75201",
  selectable: true,
};

describe("hostPublicResolveCandidates", () => {
  beforeEach(() => {
    vi.clearAllMocks();
    vi.mocked(saveHostPublicSession).mockResolvedValue("session-uuid");
  });

  it("returns host_session_id and candidates", async () => {
    vi.mocked(resolveMothershipCandidates).mockResolvedValue({
      pickup_candidates: [pickupCandidate],
      delivery_candidates: [deliveryCandidate],
      requires_selection: false,
      quote_session_id: "quote-session-1",
    });

    const result = await hostPublicResolveCandidates("CUST_002", {
      pickup_address: {
        street: "1234 Warehouse Blvd",
        city: "Los Angeles",
        state: "CA",
        zip: "90001",
      },
      delivery_address: {
        street: "5678 Distribution Dr",
        city: "Dallas",
        state: "TX",
        zip: "75201",
      },
      cargo: {
        weight: { value: 500, unit: "kg" },
        dimensions: { length: 120, width: 100, height: 150, unit: "cm" },
        pallet_count: 2,
        cargo_type: "general_freight",
      },
    });

    expect(result.host_session_id).toBe("session-uuid");
    expect(result.pickup_candidates).toHaveLength(1);
    expect(saveHostPublicSession).toHaveBeenCalledWith(
      expect.objectContaining({ customer_id: "CUST_002" }),
    );
  });
});

describe("hostPublicSubmitAndWait", () => {
  beforeEach(() => {
    vi.clearAllMocks();
    vi.mocked(loadHostPublicSession).mockResolvedValue({
      customer_id: "CUST_001",
      cargo: {
        weight: { value: 500, unit: "kg" },
        dimensions: { length: 120, width: 100, height: 150, unit: "cm" },
        pallet_count: 2,
        cargo_type: "general_freight",
      },
      pickup_draft: {
        street: "1234 Warehouse Blvd",
        city: "Los Angeles",
        state: "CA",
        zip: "90001",
      },
      delivery_draft: {
        street: "5678 Distribution Dr",
        city: "Dallas",
        state: "TX",
        zip: "75201",
      },
      quote_session_id: "quote-session-1",
      pickup_candidates: [pickupCandidate],
      delivery_candidates: [deliveryCandidate],
      created_at: new Date().toISOString(),
    });
    vi.mocked(submitQuote).mockResolvedValue({
      quote_id: "QTE_test_001",
      status: "processing",
    });
    vi.mocked(waitForQuoteDetail).mockResolvedValue({
      quote_id: "QTE_test_001",
      request_id: "req-1",
      status: "done",
      currency: "USD",
      quotes: [{ final_total: 337.06 }],
    });
    vi.mocked(prisma.quoteRecord.findFirst).mockResolvedValue({
      quoteId: "QTE_test_001",
      requestId: "req-1",
      status: "done",
      sourceType: "rpa",
      isRealtime: true,
      currency: "USD",
      confidenceScore: 0.95,
      quotesJson: [{ final_total: 337.06 }],
      validUntil: new Date("2026-06-30T10:00:00.000Z"),
      createdAt: new Date("2026-06-30T09:55:00.000Z"),
      palletCount: 2,
      cargoType: "general_freight",
      errorCode: null,
      errorMessage: null,
      isDeleted: false,
    } as never);
  });

  it("submits quote and waits for final detail", async () => {
    const detail = await hostPublicSubmitAndWait({
      host_session_id: "session-uuid",
      pickup_candidate: pickupCandidate,
      delivery_candidate: deliveryCandidate,
    });

    expect(submitQuote).toHaveBeenCalledOnce();
    expect(waitForQuoteDetail).toHaveBeenCalledWith(
      "QTE_test_001",
      "CUST_001",
    );
    expect(deleteHostPublicSession).toHaveBeenCalledWith("session-uuid");
    expect(detail.status).toBe("done");
  });

  it("rejects candidate not in session", async () => {
    await expect(
      hostPublicSubmitAndWait({
        host_session_id: "session-uuid",
        pickup_candidate: { ...pickupCandidate, option_id: "unknown" },
        delivery_candidate: deliveryCandidate,
      }),
    ).rejects.toMatchObject({ code: "ADDRESS_SESSION_MISMATCH" });
  });

  it("submitQuote 已 done 时直接查一次 DB，不走 waitForQuoteDetail", async () => {
    vi.mocked(submitQuote).mockResolvedValue({
      quote_id: "QTE_test_001",
      status: "done",
      source_type: "rpa",
      is_realtime: true,
    });

    const detail = await hostPublicSubmitAndWait({
      host_session_id: "session-uuid",
      pickup_candidate: pickupCandidate,
      delivery_candidate: deliveryCandidate,
    });

    expect(prisma.quoteRecord.findFirst).toHaveBeenCalledWith({
      where: {
        quoteId: "QTE_test_001",
        customerId: "CUST_001",
        isDeleted: false,
      },
    });
    expect(waitForQuoteDetail).not.toHaveBeenCalled();
    expect(detail.status).toBe("done");
  });
});
