import { and, eq, gte, isNull, sql } from "drizzle-orm";
import { appDb, schema } from "./appdb";

const { user, creditLedger, chatMessages } = schema;

export const URUNLER = {
  paket: { amountKurus: 29900, credits: 60, label: "Tercih Dönemi Paketi" },
  topup: { amountKurus: 12900, credits: 30, label: "+30 Kredi" },
} as const;

export const DENEME_KREDISI = 5;

// Rapor üretimi/revizyonu başına düşen kredi
export const RAPOR_KREDI = 3;

/** Paketle gelen geri bildirimli liste revizyonu hakkı */
export const MAX_REVIZYON = 2;

export type SpendResult =
  | { ok: true }
  | { ok: false; error: "INSUFFICIENT" | "DUPLICATE" };

function newId() {
  return crypto.randomUUID();
}

/**
 * Sabit tutarda kredi düşer + ledger kaydı — tek transaction, iade edilebilir.
 * Rapor üretimi ve revizyonu için (3 kredi). refId idempotency anahtarıdır:
 * aynı requestId ile ikinci çağrı DUPLICATE döner (çifte harcama olmaz).
 * Bakiye yetersizse hiçbir şey yazılmaz.
 */
export async function spendCredits(opts: {
  userId: string;
  amount: number;
  reason: "report_generate" | "report_revision";
  refId: string;
}): Promise<SpendResult> {
  try {
    await appDb.transaction(async (tx) => {
      const res = await tx
        .update(user)
        .set({
          creditBalance: sql`${user.creditBalance} - ${opts.amount}`,
          updatedAt: new Date(),
        })
        .where(
          and(eq(user.id, opts.userId), gte(user.creditBalance, opts.amount)),
        );
      if (res.rowsAffected === 0) throw new Error("INSUFFICIENT");
      await tx.insert(creditLedger).values({
        id: newId(),
        userId: opts.userId,
        delta: -opts.amount,
        reason: opts.reason,
        refId: opts.refId,
        createdAt: new Date(),
      });
    });
    return { ok: true };
  } catch (err) {
    const msg = err instanceof Error ? err.message : String(err);
    if (msg === "INSUFFICIENT") return { ok: false, error: "INSUFFICIENT" };
    if (msg.includes("UNIQUE")) return { ok: false, error: "DUPLICATE" };
    throw err;
  }
}

/**
 * 1 kredi düşer + ledger kaydı + kullanıcı mesajını persist eder — tek transaction.
 * Bakiye yetersizse hiçbir şey yazılmaz. clientMessageId unique → retry'da çifte
 * harcama yerine DUPLICATE döner.
 */
export async function spendCreditForMessage(opts: {
  userId: string;
  content: string;
  clientMessageId: string;
}): Promise<SpendResult & { messageId?: string }> {
  const messageId = newId();
  try {
    await appDb.transaction(async (tx) => {
      const res = await tx
        .update(user)
        .set({
          creditBalance: sql`${user.creditBalance} - 1`,
          updatedAt: new Date(),
        })
        .where(and(eq(user.id, opts.userId), gte(user.creditBalance, 1)));
      if (res.rowsAffected === 0) throw new Error("INSUFFICIENT");
      await tx.insert(chatMessages).values({
        id: messageId,
        userId: opts.userId,
        role: "user",
        content: opts.content,
        clientMessageId: opts.clientMessageId,
        createdAt: new Date(),
      });
      await tx.insert(creditLedger).values({
        id: newId(),
        userId: opts.userId,
        delta: -1,
        reason: "chat_message",
        refId: messageId,
        createdAt: new Date(),
      });
    });
    return { ok: true, messageId };
  } catch (err) {
    const msg = err instanceof Error ? err.message : String(err);
    if (msg === "INSUFFICIENT") return { ok: false, error: "INSUFFICIENT" };
    if (msg.includes("UNIQUE")) return { ok: false, error: "DUPLICATE" };
    throw err;
  }
}

/**
 * Kredi tanımlama (deneme, satın alma, top-up, iade). UNIQUE(reason, refId)
 * sayesinde aynı referansla ikinci çağrı sessizce no-op olur → idempotent.
 */
export async function grantCredits(opts: {
  userId: string;
  delta: number;
  reason: "trial_grant" | "purchase" | "topup" | "refund";
  refId: string;
  setHasPaket?: boolean;
}): Promise<{ granted: boolean }> {
  try {
    await appDb.transaction(async (tx) => {
      await tx.insert(creditLedger).values({
        id: newId(),
        userId: opts.userId,
        delta: opts.delta,
        reason: opts.reason,
        refId: opts.refId,
        createdAt: new Date(),
      });
      await tx
        .update(user)
        .set({
          creditBalance: sql`${user.creditBalance} + ${opts.delta}`,
          ...(opts.setHasPaket ? { hasPaket: true } : {}),
          // Kredi gelince bekleyen kredi-bitti hatırlatması iptal olur
          krediBittiAt: null,
          updatedAt: new Date(),
        })
        .where(eq(user.id, opts.userId));
    });
    return { granted: true };
  } catch (err) {
    const msg = err instanceof Error ? err.message : String(err);
    if (msg.includes("UNIQUE")) return { granted: false };
    throw err;
  }
}

/**
 * Kredi-bitti anını işaretler (48 saatlik hatırlatma e-postasının saati).
 * Yalnızca ilk bitişte yazar (kolon doluysa no-op) ve hatırlatma zaten
 * gönderildiyse yeniden kurmaz — e-posta ömür boyu 1 kez. Hata yolunda
 * çağrıldığı için asla throw etmez.
 */
export async function krediBittiIsaretle(userId: string): Promise<void> {
  try {
    await appDb
      .update(user)
      .set({ krediBittiAt: new Date() })
      .where(
        and(
          eq(user.id, userId),
          isNull(user.krediBittiAt),
          isNull(user.krediHatirlatmaGonderildiAt),
        ),
      );
  } catch (err) {
    console.error(`[kredi] kredi-bitti işaretlenemedi (${userId}):`, err);
  }
}
