import type { CaptchaProvider, VerificationConfigDashboard, VerificationConfigDashboardPatch } from '@nexumi/shared';
import type { Prisma } from '@prisma/client';
import { prisma } from '../prisma';
import { getAvailableCaptchaProviders } from '../captcha';
import {
  addJobAndAwait,
  getVerificationQueue,
  getVerificationQueueEvents
} from '../queues';

export class VerificationPanelSyncError extends Error {
  constructor(
    message: string,
    public readonly code: 'timeout' | 'not_posted' = 'not_posted'
  ) {
    super(message);
    this.name = 'VerificationPanelSyncError';
  }
}

async function ensureVerificationConfig(guildId: string) {
  await prisma.guild.upsert({ where: { id: guildId }, update: {}, create: { id: guildId } });
  return prisma.verificationConfig.upsert({
    where: { guildId },
    update: {},
    create: { guildId }
  });
}

function toDashboard(
  config: Awaited<ReturnType<typeof ensureVerificationConfig>>,
  available: CaptchaProvider[]
): VerificationConfigDashboard {
  const rawProvider = (config.captchaProvider as CaptchaProvider) ?? 'MATH';
  const captchaProvider = available.includes(rawProvider) ? rawProvider : 'MATH';
  return {
    enabled: config.enabled,
    channelId: config.channelId ?? '',
    verifiedRoleId: config.verifiedRoleId ?? '',
    unverifiedRoleId: config.unverifiedRoleId ?? '',
    mode: config.mode as VerificationConfigDashboard['mode'],
    captchaProvider,
    minAccountAgeDays: config.minAccountAgeDays,
    failAction: config.failAction as VerificationConfigDashboard['failAction'],
    altDetectionEnabled: config.altDetectionEnabled,
    altBlockOnMatch: config.altBlockOnMatch,
    altIpWindowDays: config.altIpWindowDays,
    altInviteWindowHours: config.altInviteWindowHours,
    altMaxAccountsPerInvite: config.altMaxAccountsPerInvite
  };
}

export async function getVerificationDashboard(guildId: string): Promise<{
  config: VerificationConfigDashboard;
  availableCaptchaProviders: CaptchaProvider[];
}> {
  const config = await ensureVerificationConfig(guildId);
  const availableCaptchaProviders = getAvailableCaptchaProviders();
  return {
    config: toDashboard(config, availableCaptchaProviders),
    availableCaptchaProviders
  };
}

/**
 * Dashboard has no discord.js Client, so panel posting is delegated to the bot
 * via the `verification` BullMQ queue (`verificationPanelSync`) — same pattern
 * as self-role panels / giveaways.
 */
async function syncVerificationPanel(guildId: string): Promise<void> {
  const { confirmed, result } = await addJobAndAwait<{
    panelChannelId: string;
    panelMessageId: string;
  }>(
    getVerificationQueue(),
    getVerificationQueueEvents(),
    'verificationPanelSync',
    { guildId },
    {
      jobId: `verification-panel-${guildId}-${Date.now()}`,
      removeOnComplete: true,
      removeOnFail: 25
    },
    30_000
  );

  if (!confirmed || !result?.panelMessageId) {
    throw new VerificationPanelSyncError(
      confirmed
        ? 'Verification panel job finished without posting a Discord message'
        : 'The bot did not confirm the verification panel in time. It may still be posting — check the channel shortly.',
      confirmed ? 'not_posted' : 'timeout'
    );
  }

  const config = await prisma.verificationConfig.findUnique({ where: { guildId } });
  if (!config?.panelMessageId) {
    throw new VerificationPanelSyncError(
      'Verification panel job finished without storing a Discord message id',
      'not_posted'
    );
  }
}

export async function updateVerificationDashboard(
  guildId: string,
  patch: VerificationConfigDashboardPatch
): Promise<VerificationConfigDashboard> {
  await ensureVerificationConfig(guildId);

  const available = new Set(getAvailableCaptchaProviders());
  if (patch.captchaProvider !== undefined && !available.has(patch.captchaProvider)) {
    throw new Error(`Captcha provider ${patch.captchaProvider} is not configured in .env`);
  }

  const { channelId, verifiedRoleId, unverifiedRoleId, ...rest } = patch;
  const data: Prisma.VerificationConfigUpdateInput = { ...rest };
  if (channelId !== undefined) {
    data.channelId = channelId === '' ? null : channelId;
  }
  if (verifiedRoleId !== undefined) {
    data.verifiedRoleId = verifiedRoleId === '' ? null : verifiedRoleId;
  }
  if (unverifiedRoleId !== undefined) {
    data.unverifiedRoleId = unverifiedRoleId === '' ? null : unverifiedRoleId;
  }

  const updated = await prisma.verificationConfig.update({ where: { guildId }, data });
  const dashboard = toDashboard(updated, getAvailableCaptchaProviders());

  if (dashboard.enabled && dashboard.channelId && dashboard.verifiedRoleId) {
    await syncVerificationPanel(guildId);
  }

  return dashboard;
}
