import {
  DEFAULT_AUTOMOD_RULES,
  validateAutomodRegexPatterns,
  type AutomodConfigDashboard,
  type AutomodConfigDashboardPatch,
  type AutomodRuleDashboard,
  type AutoModRuleType
} from '@nexumi/shared';
import type { Prisma } from '@prisma/client';
import { prisma } from '../prisma';
import { getAutomodQueue } from '../queues';

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

async function ensureAllRules(guildId: string): Promise<void> {
  const existing = await prisma.autoModRule.findMany({
    where: { guildId },
    select: { ruleType: true }
  });
  const have = new Set(existing.map((row) => row.ruleType));
  const missing = DEFAULT_AUTOMOD_RULES.filter((rule) => !have.has(rule.ruleType));
  if (missing.length === 0) {
    return;
  }
  await prisma.autoModRule.createMany({
    data: missing.map((rule) => ({
      guildId,
      ruleType: rule.ruleType,
      action: rule.action,
      threshold: rule.threshold ?? undefined,
      wordList: rule.wordList ?? undefined,
      enabled: ['SPAM', 'MASS_MENTION', 'INVITE_LINK', 'PHISHING'].includes(rule.ruleType)
    }))
  });
}

function parseRuleRow(row: {
  ruleType: string;
  enabled: boolean;
  action: string;
  durationMs: number | null;
  threshold: unknown;
  exceptions: unknown;
  wordList: unknown;
}): AutomodRuleDashboard {
  const threshold =
    row.threshold && typeof row.threshold === 'object' ? (row.threshold as AutomodRuleDashboard['threshold']) : {};
  const exceptions =
    row.exceptions && typeof row.exceptions === 'object'
      ? (row.exceptions as AutomodRuleDashboard['exceptions'])
      : { roleIds: [], channelIds: [] };
  const wordList =
    row.wordList && typeof row.wordList === 'object'
      ? (row.wordList as AutomodRuleDashboard['wordList'])
      : { words: [], regexPatterns: [] };

  return {
    ruleType: row.ruleType as AutoModRuleType,
    enabled: row.enabled,
    action: row.action as AutomodRuleDashboard['action'],
    durationMs: row.durationMs,
    threshold: {
      maxMentions: threshold.maxMentions,
      capsPercent: threshold.capsPercent,
      minLength: threshold.minLength,
      maxMessages: threshold.maxMessages,
      windowSeconds: threshold.windowSeconds,
      maxEmojis: threshold.maxEmojis,
      duplicateCount: threshold.duplicateCount,
      linkWhitelist: threshold.linkWhitelist ?? [],
      linkBlacklist: threshold.linkBlacklist ?? []
    },
    exceptions: {
      roleIds: Array.isArray(exceptions.roleIds) ? exceptions.roleIds.map(String) : [],
      channelIds: Array.isArray(exceptions.channelIds) ? exceptions.channelIds.map(String) : []
    },
    wordList: {
      words: Array.isArray(wordList.words) ? wordList.words.map(String) : [],
      regexPatterns: Array.isArray(wordList.regexPatterns) ? wordList.regexPatterns.map(String) : []
    }
  };
}

function toDashboard(
  config: Awaited<ReturnType<typeof ensureAutoModConfig>>,
  rules: AutomodRuleDashboard[]
): AutomodConfigDashboard {
  const order = DEFAULT_AUTOMOD_RULES.map((rule) => rule.ruleType);
  const byType = new Map(rules.map((rule) => [rule.ruleType, rule]));
  const ordered = order
    .map((type) => byType.get(type))
    .filter((rule): rule is AutomodRuleDashboard => Boolean(rule));

  return {
    enabled: config.enabled,
    antiRaidEnabled: config.antiRaidEnabled,
    antiRaidJoinThreshold: config.antiRaidJoinThreshold,
    antiRaidWindowSeconds: config.antiRaidWindowSeconds,
    antiRaidAction: (config.antiRaidAction === 'VERIFY' ? 'VERIFY' : 'LOCKDOWN') as AutomodConfigDashboard['antiRaidAction'],
    antiNukeEnabled: config.antiNukeEnabled,
    antiNukeBanThreshold: config.antiNukeBanThreshold,
    antiNukeChannelThreshold: config.antiNukeChannelThreshold,
    antiNukeWindowSeconds: config.antiNukeWindowSeconds,
    lockdownActive: config.lockdownActive,
    rules: ordered
  };
}

export async function getAutomodDashboard(guildId: string): Promise<AutomodConfigDashboard> {
  const config = await ensureAutoModConfig(guildId);
  await ensureAllRules(guildId);
  const rows = await prisma.autoModRule.findMany({ where: { guildId } });
  return toDashboard(config, rows.map(parseRuleRow));
}

export async function updateAutomodDashboard(
  guildId: string,
  patch: AutomodConfigDashboardPatch
): Promise<AutomodConfigDashboard> {
  const before = await getAutomodDashboard(guildId);

  if (patch.rules) {
    for (const rule of patch.rules) {
      const bad = validateAutomodRegexPatterns(rule.wordList?.regexPatterns ?? []);
      if (bad) {
        throw new Error(`Invalid regex pattern: ${bad}`);
      }
    }
  }

  const { rules, ...configPatch } = patch;
  const data: Prisma.AutoModConfigUpdateInput = { ...configPatch };

  await prisma.$transaction(async (tx) => {
    if (Object.keys(data).length > 0) {
      await tx.autoModConfig.update({ where: { guildId }, data });
    }
    if (rules) {
      for (const rule of rules) {
        await tx.autoModRule.upsert({
          where: { guildId_ruleType: { guildId, ruleType: rule.ruleType } },
          create: {
            guildId,
            ruleType: rule.ruleType,
            enabled: rule.enabled,
            action: rule.action,
            durationMs: rule.durationMs ?? null,
            threshold: rule.threshold as Prisma.InputJsonValue,
            exceptions: rule.exceptions as Prisma.InputJsonValue,
            wordList: rule.wordList as Prisma.InputJsonValue
          },
          update: {
            enabled: rule.enabled,
            action: rule.action,
            durationMs: rule.durationMs ?? null,
            threshold: rule.threshold as Prisma.InputJsonValue,
            exceptions: rule.exceptions as Prisma.InputJsonValue,
            wordList: rule.wordList as Prisma.InputJsonValue
          }
        });
      }
    }
  });

  const after = await getAutomodDashboard(guildId);

  // Lockdown permission restore must run on the bot (discord.js), not in WebUI.
  if (before.lockdownActive && after.lockdownActive === false) {
    await getAutomodQueue().add(
      'deactivateLockdown',
      { guildId },
      { removeOnComplete: 100, removeOnFail: 50 }
    );
  } else if (!before.lockdownActive && after.lockdownActive === true && after.antiRaidAction === 'LOCKDOWN') {
    await getAutomodQueue().add(
      'activateLockdown',
      { guildId },
      { removeOnComplete: 100, removeOnFail: 50 }
    );
  }

  return after;
}
