import { prisma } from "@/lib/prisma";
import { acquireDbLock, releaseDbLock } from "@/lib/db-lock";
import { getBullConnection } from "@/lib/redis-bull";

const LOCK_TTL_SECONDS = 300; // 5 menit
const BATCH_SIZE = 500;

// ─────────────────────────────────────────────────────────
// [3.2] Interest Engine (PRO+ hasInterest = true)
// ─────────────────────────────────────────────────────────
export async function runInterestEngine(): Promise<{
  processed: number;
  credited: number;
  totalInterest: number;
}> {
  const locked = await acquireDbLock("cron:interest", LOCK_TTL_SECONDS);
  if (!locked) {
    console.log("[InterestWorker] Skipped — lock held by another instance");
    return { processed: 0, credited: 0, totalInterest: 0 };
  }

  let cursor = 0;
  let processed = 0;
  let credited = 0;
  let totalInterest = 0;

  try {
    const appConfig = await prisma.appConfig.findUnique({ where: { id: "global-config" } });
    const configData = (appConfig?.data ?? {}) as Record<string, unknown>;
    const interestRate = typeof configData.interestRate === "number" ? configData.interestRate : 2;

    while (true) {
      const children = await prisma.child.findMany({
        where: {
          deletedAt: null,
          savingsBalance: { gt: 0 },
          familySpace: {
            subscription: {
              status: { in: ["TRIAL", "PRO", "EDUCATOR", "SCHOOL"] },
              plan: { NOT: { limits: {} } },
            },
          },
        },
        include: {
          familySpace: {
            include: {
              subscription: { include: { plan: true } },
            },
          },
        },
        skip: cursor,
        take: BATCH_SIZE,
        orderBy: { createdAt: "asc" },
      });

      if (children.length === 0) break;

      for (const child of children) {
        const limits = child.familySpace.subscription?.plan?.limits as Record<string, unknown> | null;
        if (!limits?.hasInterest) {
          processed++;
          continue;
        }

        const rate = typeof limits.interestRate === "number" ? limits.interestRate : interestRate;
        const interest = Math.floor(child.savingsBalance * (rate / 100));
        if (interest <= 0) {
          processed++;
          continue;
        }

        try {
          await prisma.$transaction(async (tx) => {
            const before = await tx.child.findUniqueOrThrow({
              where: { id: child.id },
              select: { savingsBalance: true },
            });

            const updated = await tx.child.update({
              where: { id: child.id },
              data: { savingsBalance: { increment: interest } },
              select: { savingsBalance: true },
            });

            await tx.transactionLedger.create({
              data: {
                familySpaceId: child.familySpaceId,
                childId: child.id,
                type: "INTEREST",
                amount: interest,
                balanceBefore: before.savingsBalance,
                balanceAfter: updated.savingsBalance,
                description: `Bunga tabungan ${rate}% harian`,
              },
            });
          });

          credited++;
          totalInterest += interest;
        } catch (err) {
          console.error(`[InterestWorker] Failed for child ${child.id}:`, err);
        }

        processed++;
      }

      cursor += BATCH_SIZE;
      if (children.length < BATCH_SIZE) break;
    }

    console.log(
      `[InterestWorker] Done — processed=${processed}, credited=${credited}, totalInterest=${totalInterest}`
    );
  } finally {
    await releaseDbLock("cron:interest");
  }

  return { processed, credited, totalInterest };
}

// ─────────────────────────────────────────────────────────
// [3.3] Tax Engine (PRO+ hasTax = true)
// ─────────────────────────────────────────────────────────
export async function runTaxEngine(): Promise<{
  processed: number;
  taxed: number;
  totalTax: number;
}> {
  const locked = await acquireDbLock("cron:tax", LOCK_TTL_SECONDS);
  if (!locked) {
    console.log("[TaxWorker] Skipped — lock held by another instance");
    return { processed: 0, taxed: 0, totalTax: 0 };
  }

  let cursor = 0;
  let processed = 0;
  let taxed = 0;
  let totalTax = 0;

  const now = new Date();
  const monthStart = new Date(now.getFullYear(), now.getMonth() - 1, 1);
  const monthEnd = new Date(now.getFullYear(), now.getMonth(), 1);

  try {
    while (true) {
      const children = await prisma.child.findMany({
        where: {
          deletedAt: null,
          familySpace: {
            subscription: {
              status: { in: ["TRIAL", "PRO", "EDUCATOR", "SCHOOL"] },
              plan: { NOT: { limits: {} } },
            },
          },
        },
        include: {
          familySpace: {
            include: { subscription: { include: { plan: true } } },
          },
        },
        skip: cursor,
        take: BATCH_SIZE,
        orderBy: { createdAt: "asc" },
      });

      if (children.length === 0) break;

      for (const child of children) {
        const limits = child.familySpace.subscription?.plan?.limits as Record<string, unknown> | null;
        if (!limits?.hasTax) {
          processed++;
          continue;
        }

        const taxRate = typeof limits.taxRate === "number" ? limits.taxRate : 5;

        const lastMonthRewards = await prisma.transactionLedger.aggregate({
          where: {
            childId: child.id,
            type: "TASK_REWARD",
            createdAt: { gte: monthStart, lt: monthEnd },
          },
          _sum: { amount: true },
        });

        const rewardTotal = lastMonthRewards._sum.amount ?? 0;
        const taxAmount = Math.floor(rewardTotal * (taxRate / 100));
        if (taxAmount <= 0) {
          processed++;
          continue;
        }

        try {
          await prisma.$transaction(async (tx) => {
            const current = await tx.child.findUniqueOrThrow({
              where: { id: child.id },
              select: { balance: true },
            });

            if (current.balance < taxAmount) return;

            const updated = await tx.child.update({
              where: { id: child.id },
              data: { balance: { decrement: taxAmount } },
              select: { balance: true },
            });

            await tx.transactionLedger.create({
              data: {
                familySpaceId: child.familySpaceId,
                childId: child.id,
                type: "TAX",
                amount: -taxAmount,
                balanceBefore: current.balance,
                balanceAfter: updated.balance,
                description: `Pajak virtual ${taxRate}% dari reward bulan ${monthStart.toLocaleDateString("id-ID", { month: "long", year: "numeric" })}`,
                refId: `tax-${monthStart.toISOString().substring(0, 7)}`,
              },
            });
          });

          taxed++;
          totalTax += taxAmount;
        } catch (err) {
          console.error(`[TaxWorker] Failed for child ${child.id}:`, err);
        }

        processed++;
      }

      cursor += BATCH_SIZE;
      if (children.length < BATCH_SIZE) break;
    }

    console.log(`[TaxWorker] Done — processed=${processed}, taxed=${taxed}, totalTax=${totalTax}`);
  } finally {
    await releaseDbLock("cron:tax");
  }

  return { processed, taxed, totalTax };
}

// ─────────────────────────────────────────────────────────
// Legacy BullMQ worker (hanya aktif jika Redis tersedia)
// ─────────────────────────────────────────────────────────
export function startInterestWorker() {
  const connection = getBullConnection();
  if (!connection) {
    console.warn("[InterestWorker] Redis not available — using cron endpoints instead (/api/cron/interest, /api/cron/tax)");
    return null;
  }

  try {
    // eslint-disable-next-line @typescript-eslint/no-var-requires
    const { Worker } = require("bullmq") as typeof import("bullmq");
    const worker = new Worker(
      "interest",
      async (job: import("bullmq").Job) => {
        console.log(`[InterestWorker] Job ${job.id} started`);
        if (job.data?.type === "TAX") return runTaxEngine();
        return runInterestEngine();
      },
      { connection }
    );

    worker.on("failed", (job, err) => {
      console.error(`[InterestWorker] Job ${job?.id} failed:`, err);
    });

    return worker;
  } catch {
    return null;
  }
}
