import { QuotaExtensionRequestStatus } from "@prisma/client";
import { MAX_ADMIN_API_RATE_BONUS } from "./api-rate-limit";
import { prisma } from "./db";
import { getPlanLimits } from "./plans";

export const QUOTA_EXTENSION_ANNUAL_LIMIT = 5;
/** Max bonus videos an admin may grant per approval. */
export const MAX_ADMIN_BONUS_QUOTA = 50;

export const EXTENSION_KIND = {
  VIDEO_QUOTA: "VIDEO_QUOTA",
  API_RATE_LIMIT: "API_RATE_LIMIT",
} as const;

export type ExtensionKind = (typeof EXTENSION_KIND)[keyof typeof EXTENSION_KIND];

function getCalendarYearBounds(year = new Date().getFullYear()) {
  return {
    start: new Date(year, 0, 1),
    end: new Date(year + 1, 0, 1),
  };
}

export async function getQuotaExtensionUsage(
  userId: string,
  kind: ExtensionKind = EXTENSION_KIND.VIDEO_QUOTA,
) {
  const { start, end } = getCalendarYearBounds();

  const requests = await prisma.quotaExtensionRequest.findMany({
    where: {
      userId,
      kind,
      requestedAt: { gte: start, lt: end },
    },
    orderBy: { requestedAt: "desc" },
  });

  const used = requests.filter((r) => r.status !== QuotaExtensionRequestStatus.REJECTED).length;

  return {
    used,
    limit: QUOTA_EXTENSION_ANNUAL_LIMIT,
    remaining: Math.max(0, QUOTA_EXTENSION_ANNUAL_LIMIT - used),
    kind,
    requests: requests.map((r) => ({
      id: r.id,
      kind: r.kind,
      status: r.status,
      message: r.message,
      requestedAt: r.requestedAt.toISOString(),
      processedAt: r.processedAt?.toISOString() ?? null,
      adminNote: r.adminNote,
    })),
  };
}

export async function createQuotaExtensionRequest(
  userId: string,
  message = "",
  kind: ExtensionKind = EXTENSION_KIND.VIDEO_QUOTA,
) {
  const user = await prisma.user.findUniqueOrThrow({ where: { id: userId } });

  if (user.plan !== "PREMIUM") {
    throw new Error("Only Pro subscribers can request quota resets or extensions.");
  }

  const usage = await getQuotaExtensionUsage(userId, kind);
  if (usage.remaining <= 0) {
    throw new Error(
      `You have used all ${QUOTA_EXTENSION_ANNUAL_LIMIT} ${
        kind === EXTENSION_KIND.API_RATE_LIMIT ? "API rate limit" : "quota"
      } extension requests for this year.`,
    );
  }

  const pending = await prisma.quotaExtensionRequest.findFirst({
    where: { userId, kind, status: QuotaExtensionRequestStatus.PENDING },
  });
  if (pending) {
    throw new Error("You already have a pending request. Please wait for it to be processed.");
  }

  const request = await prisma.quotaExtensionRequest.create({
    data: {
      userId,
      kind,
      message: message.trim().slice(0, 1000),
      status: QuotaExtensionRequestStatus.PENDING,
    },
  });

  const updatedUsage = await getQuotaExtensionUsage(userId, kind);

  return {
    requestId: request.id,
    ...updatedUsage,
  };
}

/** Call when approving a request in the database (admin / support). */
export async function approveQuotaExtensionRequest(
  requestId: string,
  options?: { bonusQuota?: number; bonusRateLimit?: number; adminNote?: string },
) {
  const request = await prisma.quotaExtensionRequest.findUniqueOrThrow({
    where: { id: requestId },
    include: { user: true },
  });

  if (request.status !== QuotaExtensionRequestStatus.PENDING) {
    throw new Error("Request is not pending.");
  }

  if (request.kind === EXTENSION_KIND.API_RATE_LIMIT) {
    const bonus = Math.min(
      MAX_ADMIN_API_RATE_BONUS,
      Math.max(0, Math.floor(options?.bonusRateLimit ?? 30)),
    );

    await prisma.$transaction([
      prisma.quotaExtensionRequest.update({
        where: { id: requestId },
        data: {
          status: QuotaExtensionRequestStatus.APPROVED,
          processedAt: new Date(),
          adminNote: options?.adminNote?.trim().slice(0, 500) ?? null,
        },
      }),
      prisma.user.update({
        where: { id: request.userId },
        data: {
          apiRateLimitBonus: request.user.apiRateLimitBonus + bonus,
        },
      }),
    ]);
    return;
  }

  const bonus = Math.min(
    MAX_ADMIN_BONUS_QUOTA,
    Math.max(0, Math.floor(options?.bonusQuota ?? 0)),
  );

  await prisma.$transaction([
    prisma.quotaExtensionRequest.update({
      where: { id: requestId },
      data: {
        status: QuotaExtensionRequestStatus.APPROVED,
        processedAt: new Date(),
        adminNote: options?.adminNote?.trim().slice(0, 500) ?? null,
      },
    }),
    prisma.user.update({
      where: { id: request.userId },
      data: {
        videosUsed: 0,
        bonusQuota: request.user.bonusQuota + bonus,
      },
    }),
  ]);
}

export async function rejectQuotaExtensionRequest(requestId: string, adminNote?: string) {
  const request = await prisma.quotaExtensionRequest.findUniqueOrThrow({
    where: { id: requestId },
  });

  if (request.status !== QuotaExtensionRequestStatus.PENDING) {
    throw new Error("Request is not pending.");
  }

  await prisma.quotaExtensionRequest.update({
    where: { id: requestId },
    data: {
      status: QuotaExtensionRequestStatus.REJECTED,
      processedAt: new Date(),
      adminNote: adminNote?.trim().slice(0, 500) ?? null,
    },
  });
}

export function getEffectiveQuotaLimit(plan: Parameters<typeof getPlanLimits>[0], bonusQuota: number) {
  return getPlanLimits(plan).monthlyQuota + bonusQuota;
}
