import { createHash, randomBytes, randomInt } from 'node:crypto';
import type { CaptchaProvider, VerificationFailAction, VerificationMode } from '@nexumi/shared';
import type { PrismaClient } from '@prisma/client';
import type { Redis } from 'ioredis';
import { ensureGuild } from '../../guild.js';

const CAPTCHA_PREFIX = 'verify:captcha:';

export type VerificationConfigUpdate = {
  enabled: boolean;
  channelId: string;
  verifiedRoleId: string;
  unverifiedRoleId?: string | null;
  mode: VerificationMode;
  captchaProvider?: CaptchaProvider;
  minAccountAgeDays: number;
  failAction: VerificationFailAction;
  altDetectionEnabled?: boolean;
  altBlockOnMatch?: boolean;
  altIpWindowDays?: number;
  altInviteWindowHours?: number;
  altMaxAccountsPerInvite?: number;
};

export async function getVerificationConfig(prisma: PrismaClient, guildId: string) {
  await ensureGuild(prisma, guildId);
  let config = await prisma.verificationConfig.findUnique({ where: { guildId } });
  if (!config) {
    config = await prisma.verificationConfig.create({ data: { guildId } });
  }
  return config;
}

export async function updateVerificationConfig(
  prisma: PrismaClient,
  guildId: string,
  data: VerificationConfigUpdate
) {
  await ensureGuild(prisma, guildId);
  return prisma.verificationConfig.upsert({
    where: { guildId },
    update: data,
    create: { guildId, ...data }
  });
}

export function verificationButtonId(guildId: string): string {
  return `verify:button:${guildId}`;
}

export function verificationCaptchaButtonId(guildId: string): string {
  return `verify:captcha:${guildId}`;
}

export function isVerificationButton(customId: string): boolean {
  return customId.startsWith('verify:button:') || customId.startsWith('verify:captcha:');
}

export function parseVerificationButton(customId: string): {
  mode: 'button' | 'captcha';
  guildId: string;
} | null {
  if (customId.startsWith('verify:button:')) {
    return { mode: 'button', guildId: customId.slice('verify:button:'.length) };
  }
  if (customId.startsWith('verify:captcha:')) {
    return { mode: 'captcha', guildId: customId.slice('verify:captcha:'.length) };
  }
  return null;
}

export type CaptchaChallenge = {
  token: string;
  guildId: string;
  userId: string;
  provider: CaptchaProvider;
  question?: string;
  answerHash?: string;
};

export async function createCaptchaChallenge(
  redis: Redis,
  guildId: string,
  userId: string,
  provider: CaptchaProvider = 'MATH'
): Promise<CaptchaChallenge> {
  const token = randomBytes(24).toString('hex');
  const challenge: CaptchaChallenge = {
    token,
    guildId,
    userId,
    provider
  };

  if (provider === 'MATH') {
    const a = randomInt(2, 12);
    const b = randomInt(2, 12);
    challenge.question = `${a} + ${b}`;
    challenge.answerHash = createHash('sha256').update(String(a + b)).digest('hex');
  }

  await redis.set(`${CAPTCHA_PREFIX}${token}`, JSON.stringify(challenge), 'EX', 600);
  return challenge;
}

export async function getCaptchaChallenge(
  redis: Redis,
  token: string
): Promise<CaptchaChallenge | null> {
  const raw = await redis.get(`${CAPTCHA_PREFIX}${token}`);
  if (!raw) {
    return null;
  }
  const parsed = JSON.parse(raw) as CaptchaChallenge;
  if (!parsed.provider) {
    parsed.provider = 'MATH';
  }
  return parsed;
}

export async function deleteCaptchaChallenge(redis: Redis, token: string): Promise<void> {
  await redis.del(`${CAPTCHA_PREFIX}${token}`);
}

export function hashCaptchaAnswer(answer: string): string {
  return createHash('sha256').update(answer.trim()).digest('hex');
}

export function accountAgeDays(userCreatedAt: Date): number {
  const ms = Date.now() - userCreatedAt.getTime();
  return Math.floor(ms / (1000 * 60 * 60 * 24));
}

export function hashClientSignal(salt: string, value: string): string {
  return createHash('sha256').update(`${salt}:${value}`).digest('hex');
}
