import { IDEMPOTENCY_TTL_MS } from "@/lib/constants/quote";
import { prisma } from "@/lib/prisma";
import { getL1, setL1 } from "@/modules/cache/redis-cache";

export type IdempotencyHit = {
  hit: true;
  quoteId: string;
  response: unknown;
};

export type IdempotencyMiss = {
  hit: false;
};

export type IdempotencyResult = IdempotencyHit | IdempotencyMiss;

/** 查 L1 Redis → DB 双保险，24h 内同 request_id 返回同一 quote_id */
export async function checkIdempotency(
  requestId: string,
): Promise<IdempotencyResult> {
  const cached = await getL1<{ quoteId: string; response: unknown }>(
    requestId,
  );
  if (cached?.quoteId) {
    return {
      hit: true,
      quoteId: cached.quoteId,
      response: cached.response,
    };
  }

  const record = await prisma.idempotencyRecord.findUnique({
    where: { requestId },
    select: { quoteId: true },
  });

  if (!record) {
    return { hit: false };
  }

  const quote = await prisma.quoteRecord.findUnique({
    where: { quoteId: record.quoteId },
    select: {
      quoteId: true,
      status: true,
      sourceType: true,
      isRealtime: true,
    },
  });

  const response = {
    quote_id: record.quoteId,
    status: quote?.status ?? "done",
  };

  await setL1(requestId, { quoteId: record.quoteId, response });

  return {
    hit: true,
    quoteId: record.quoteId,
    response,
  };
}

/** 写入 DB + Redis L1，处理唯一键冲突 */
export async function saveIdempotency(
  requestId: string,
  customerId: string,
  quoteId: string,
  response: unknown,
): Promise<void> {
  const expireAt = new Date(Date.now() + IDEMPOTENCY_TTL_MS);

  try {
    await prisma.idempotencyRecord.create({
      data: {
        requestId,
        customerId,
        quoteId,
        expireAt,
      },
    });
  } catch (error: unknown) {
    const prismaError = error as { code?: string };
    if (prismaError.code === "P2002") {
      const existing = await prisma.idempotencyRecord.findUnique({
        where: { requestId },
        select: { quoteId: true },
      });
      if (existing) {
        return;
      }
    }
    throw error;
  }

  await setL1(requestId, { quoteId, response });
}
