import "server-only";

import type { RowDataPacket } from "mysql2/promise";

import { ensureOfficialMailSchema, getDbPool } from "@/lib/db";
import {
  ensureMailcowDomainQuotaLimits,
  getMailcowPlanQuotaConfig,
  setMailcowMailboxQuota,
} from "@/lib/mailcow";
import { hasOperationalBenefitByOwnerUserId } from "@/lib/mail-operational-benefit";

export type MailPlanQuotaTier = "free" | "growth" | "business";

const DEFAULT_QUOTA_DOWNGRADE_GRACE_DAYS = 7;

type OwnerPlanRow = RowDataPacket & {
  email: string;
  polar_current_period_end: Date | null;
  polar_status: string | null;
  toss_last_charged_at: Date | null;
  toss_member_access_ends_at: Date | null;
  plan_tier: "growth" | "business" | null;
  toss_status: "pending" | "active" | "paused" | "cancelled" | null;
};

type MailboxEmailRow = RowDataPacket & {
  email: string;
};

export type MailPlanQuotaSyncSummary = {
  domainLimitsUpdated: number;
  mailboxMissing: number;
  mailboxUnchanged: number;
  mailboxUpdated: number;
  ownerUserId: number;
  quotaMb: number;
  tier: MailPlanQuotaTier;
};

export type MailPlanQuotaReconcileSummary = {
  attemptedOwners: number;
  failedOwnerIds: number[];
  failedOwners: number;
  mailboxMissing: number;
  mailboxUpdated: number;
  successfulOwners: number;
};

export function getMailPlanMailboxQuotaMb(tier: MailPlanQuotaTier) {
  const config = getMailcowPlanQuotaConfig();

  switch (tier) {
    case "business":
      return config.businessMailboxQuotaMb;
    case "growth":
      return config.growthMailboxQuotaMb;
    default:
      return config.freeMailboxQuotaMb;
  }
}

function getQuotaDowngradeGraceMs() {
  const configuredDays = Number(
    process.env.MAILCOW_QUOTA_DOWNGRADE_GRACE_DAYS ??
      DEFAULT_QUOTA_DOWNGRADE_GRACE_DAYS,
  );
  const graceDays =
    Number.isFinite(configuredDays) && configuredDays >= 0
      ? configuredDays
      : DEFAULT_QUOTA_DOWNGRADE_GRACE_DAYS;

  return Math.round(graceDays * 24 * 60 * 60 * 1000);
}

export async function getMailPlanQuotaTierForOwnerUserId(
  ownerUserId: number,
): Promise<MailPlanQuotaTier> {
  const [rows] = await getDbPool().query<OwnerPlanRow[]>(
    `
      SELECT
        u.email,
        toss.status AS toss_status,
        toss.plan_tier,
        toss.last_charged_at AS toss_last_charged_at,
        toss.member_access_ends_at AS toss_member_access_ends_at,
        polar.status AS polar_status,
        polar.current_period_end AS polar_current_period_end
      FROM users u
      LEFT JOIN mailbox_toss_pay_subscriptions toss
        ON toss.owner_user_id = u.id
      LEFT JOIN mailbox_polar_subscriptions polar
        ON polar.owner_user_id = u.id
      WHERE u.id = ?
      LIMIT 1
    `,
    [ownerUserId],
  );
  const owner = rows[0];

  if (!owner) {
    throw new Error("mail-plan-owner-not-found");
  }

  if (await hasOperationalBenefitByOwnerUserId(ownerUserId)) {
    return "growth";
  }

  const tossPaidPlanActive =
    owner.toss_status === "active" && Boolean(owner.toss_last_charged_at);
  const polarPaidPlanActive =
    owner.polar_status === "active" ||
    (owner.polar_status === "canceled" &&
      Boolean(owner.polar_current_period_end) &&
      owner.polar_current_period_end!.getTime() > Date.now());
  const cancelledPlanQuotaEndsAt = owner.toss_member_access_ends_at
    ? owner.toss_member_access_ends_at.getTime() + getQuotaDowngradeGraceMs()
    : null;
  const cancelledPlanStillAvailable =
    owner.toss_status === "cancelled" &&
    Boolean(owner.toss_last_charged_at) &&
    cancelledPlanQuotaEndsAt !== null &&
    cancelledPlanQuotaEndsAt > Date.now();

  if (!tossPaidPlanActive && !polarPaidPlanActive && !cancelledPlanStillAvailable) {
    return "free";
  }

  return owner.plan_tier === "business" ? "business" : "growth";
}

async function getOwnerMailboxEmails(
  ownerUserId: number,
  includeManagedMembers: boolean,
) {
  const [representativeRows] = await getDbPool().query<MailboxEmailRow[]>(
    `
      SELECT LOWER(email) AS email
      FROM mailboxes
      WHERE user_id = ?
        AND status <> 'disabled'
      ORDER BY id ASC
    `,
    [ownerUserId],
  );
  const memberRows = includeManagedMembers
    ? (
        await getDbPool().query<MailboxEmailRow[]>(
          `
            SELECT LOWER(email) AS email
            FROM managed_team_mailboxes
            WHERE owner_user_id = ?
              AND status = 'active'
            ORDER BY id ASC
          `,
          [ownerUserId],
        )
      )[0]
    : [];

  return [
    ...new Set(
      [...representativeRows, ...memberRows]
        .map((row) => row.email.trim().toLowerCase())
        .filter(Boolean),
    ),
  ];
}

export async function syncMailcowMailboxQuotasForOwnerUserId(
  ownerUserId: number,
  tierOverride?: MailPlanQuotaTier,
): Promise<MailPlanQuotaSyncSummary> {
  if (!Number.isInteger(ownerUserId) || ownerUserId <= 0) {
    throw new Error("mail-plan-owner-invalid");
  }

  await ensureOfficialMailSchema();

  const tier =
    tierOverride ?? (await getMailPlanQuotaTierForOwnerUserId(ownerUserId));
  const quotaMb = getMailPlanMailboxQuotaMb(tier);
  const mailboxEmails = await getOwnerMailboxEmails(ownerUserId, tier !== "free");
  const domains = [
    ...new Set(
      mailboxEmails
        .map((email) => email.split("@")[1]?.trim().toLowerCase())
        .filter((domain): domain is string => Boolean(domain)),
    ),
  ];
  const summary: MailPlanQuotaSyncSummary = {
    domainLimitsUpdated: 0,
    mailboxMissing: 0,
    mailboxUnchanged: 0,
    mailboxUpdated: 0,
    ownerUserId,
    quotaMb,
    tier,
  };

  for (const domain of domains) {
    if ((await ensureMailcowDomainQuotaLimits(domain)) === "updated") {
      summary.domainLimitsUpdated += 1;
    }
  }

  for (const mailboxEmail of mailboxEmails) {
    const result = await setMailcowMailboxQuota(mailboxEmail, quotaMb);

    if (result === "updated") summary.mailboxUpdated += 1;
    if (result === "unchanged") summary.mailboxUnchanged += 1;
    if (result === "missing") summary.mailboxMissing += 1;
  }

  return summary;
}

export async function reconcileMailcowMailboxQuotas(
  limit = 1000,
): Promise<MailPlanQuotaReconcileSummary> {
  await ensureOfficialMailSchema();

  const normalizedLimit = Math.max(1, Math.min(5000, Math.floor(limit)));
  const [owners] = await getDbPool().query<(RowDataPacket & { owner_user_id: number })[]>(
    `
      SELECT DISTINCT d.user_id AS owner_user_id
      FROM domains d
      INNER JOIN mailboxes m
        ON m.domain_id = d.id
       AND m.user_id = d.user_id
       AND m.status <> 'disabled'
      WHERE d.mailcow_cleanup_at IS NULL
      ORDER BY d.user_id ASC
      LIMIT ?
    `,
    [normalizedLimit],
  );
  const summary: MailPlanQuotaReconcileSummary = {
    attemptedOwners: owners.length,
    failedOwnerIds: [],
    failedOwners: 0,
    mailboxMissing: 0,
    mailboxUpdated: 0,
    successfulOwners: 0,
  };

  for (const owner of owners) {
    try {
      const ownerSummary = await syncMailcowMailboxQuotasForOwnerUserId(
        owner.owner_user_id,
      );
      summary.successfulOwners += 1;
      summary.mailboxMissing += ownerSummary.mailboxMissing;
      summary.mailboxUpdated += ownerSummary.mailboxUpdated;
    } catch (error) {
      summary.failedOwners += 1;
      summary.failedOwnerIds.push(owner.owner_user_id);
      console.error(
        `[mailbox-quota] owner=${owner.owner_user_id} sync failed`,
        error instanceof Error ? error.message : error,
      );
    }
  }

  return summary;
}
