import { z } from 'zod';

export const AutoModRuleTypeSchema = z.enum([
  'SPAM',
  'MASS_MENTION',
  'CAPS',
  'INVITE_LINK',
  'EXTERNAL_LINK',
  'WORD_FILTER',
  'DUPLICATE',
  'EMOJI_SPAM',
  'ZALGO',
  'PHISHING'
]);
export type AutoModRuleType = z.infer<typeof AutoModRuleTypeSchema>;

export const AutoModActionSchema = z.enum(['DELETE', 'WARN', 'TIMEOUT', 'KICK', 'BAN']);
export type AutoModAction = z.infer<typeof AutoModActionSchema>;

export const AutoModExceptionsSchema = z.object({
  roleIds: z.array(z.string()).default([]),
  channelIds: z.array(z.string()).default([])
});
export type AutoModExceptions = z.infer<typeof AutoModExceptionsSchema>;

export const AutoModThresholdSchema = z.object({
  maxMentions: z.number().int().positive().optional(),
  capsPercent: z.number().min(0).max(100).optional(),
  minLength: z.number().int().nonnegative().optional(),
  maxMessages: z.number().int().positive().optional(),
  windowSeconds: z.number().int().positive().optional(),
  maxEmojis: z.number().int().positive().optional(),
  duplicateCount: z.number().int().positive().optional(),
  linkWhitelist: z.array(z.string()).optional(),
  linkBlacklist: z.array(z.string()).optional()
});
export type AutoModThreshold = z.infer<typeof AutoModThresholdSchema>;

export const AutoModWordListSchema = z.object({
  words: z.array(z.string()).default([]),
  regexPatterns: z.array(z.string()).default([])
});
export type AutoModWordList = z.infer<typeof AutoModWordListSchema>;

export const DEFAULT_AUTOMOD_RULES: Array<{
  ruleType: AutoModRuleType;
  action: AutoModAction;
  threshold?: AutoModThreshold;
  wordList?: AutoModWordList;
}> = [
  {
    ruleType: 'SPAM',
    action: 'DELETE',
    threshold: { maxMessages: 5, windowSeconds: 5 }
  },
  {
    ruleType: 'MASS_MENTION',
    action: 'DELETE',
    threshold: { maxMentions: 5 }
  },
  {
    ruleType: 'CAPS',
    action: 'DELETE',
    threshold: { capsPercent: 70, minLength: 10 }
  },
  {
    ruleType: 'INVITE_LINK',
    action: 'DELETE'
  },
  {
    ruleType: 'EXTERNAL_LINK',
    action: 'DELETE',
    threshold: { linkWhitelist: ['discord.com', 'discord.gg'] }
  },
  {
    ruleType: 'WORD_FILTER',
    action: 'DELETE',
    wordList: { words: [], regexPatterns: [] }
  },
  {
    ruleType: 'DUPLICATE',
    action: 'DELETE',
    threshold: { duplicateCount: 3, windowSeconds: 30 }
  },
  {
    ruleType: 'EMOJI_SPAM',
    action: 'DELETE',
    threshold: { maxEmojis: 10 }
  },
  {
    ruleType: 'ZALGO',
    action: 'DELETE'
  },
  {
    ruleType: 'PHISHING',
    action: 'DELETE'
  }
];

export const LogEventTypeSchema = z.enum([
  'MESSAGE_EDIT',
  'MESSAGE_DELETE',
  'MESSAGE_BULK_DELETE',
  'MEMBER_JOIN',
  'MEMBER_LEAVE',
  'MEMBER_BAN',
  'MEMBER_UNBAN',
  'MEMBER_UPDATE',
  'CHANNEL_CREATE',
  'CHANNEL_DELETE',
  'CHANNEL_UPDATE',
  'VOICE_JOIN',
  'VOICE_LEAVE',
  'VOICE_MOVE',
  'INVITE_CREATE',
  'EMOJI_CREATE',
  'EMOJI_UPDATE',
  'EMOJI_DELETE',
  'STICKER_CREATE',
  'STICKER_UPDATE',
  'STICKER_DELETE',
  'THREAD_CREATE',
  'THREAD_UPDATE',
  'THREAD_DELETE',
  'GUILD_UPDATE',
  'MOD_ACTION',
  'VERIFICATION_FAIL'
]);
export type LogEventType = z.infer<typeof LogEventTypeSchema>;

export const WelcomeMessageTypeSchema = z.enum(['TEXT', 'EMBED', 'IMAGE', 'COMPONENTS_V2']);
export type WelcomeMessageType = z.infer<typeof WelcomeMessageTypeSchema>;

export const LeaveMessageTypeSchema = z.enum(['TEXT', 'EMBED', 'COMPONENTS_V2']);
export type LeaveMessageType = z.infer<typeof LeaveMessageTypeSchema>;

export const WelcomeEmbedSchema = z.object({
  title: z.string().max(256).optional(),
  description: z.string().max(4096).optional(),
  /** Clickable title URL. */
  url: z.string().max(2048).optional(),
  color: z.number().int().optional(),
  authorName: z.string().max(256).optional(),
  authorIconUrl: z.string().max(2048).optional(),
  authorUrl: z.string().max(2048).optional(),
  thumbnailUrl: z.string().max(2048).optional(),
  imageUrl: z.string().max(2048).optional(),
  footerText: z.string().max(2048).optional(),
  footerIconUrl: z.string().max(2048).optional(),
  /** When true, Discord shows the current time in the footer area. */
  timestamp: z.boolean().optional()
});
export type WelcomeEmbed = z.infer<typeof WelcomeEmbedSchema>;

/** True when the embed has any user-facing content beyond an optional color. */
export function embedHasContent(embed: WelcomeEmbed | null | undefined): boolean {
  if (!embed) {
    return false;
  }
  return Boolean(
    embed.title?.trim() ||
      embed.description?.trim() ||
      embed.url?.trim() ||
      embed.authorName?.trim() ||
      embed.authorIconUrl?.trim() ||
      embed.authorUrl?.trim() ||
      embed.thumbnailUrl?.trim() ||
      embed.imageUrl?.trim() ||
      embed.footerText?.trim() ||
      embed.footerIconUrl?.trim() ||
      embed.timestamp
  );
}

/** Text/URL fields of {@link WelcomeEmbed} that pass through {@link mapEmbedTextFields}. */
export type EmbedTextField =
  | 'title'
  | 'description'
  | 'url'
  | 'authorName'
  | 'authorIconUrl'
  | 'authorUrl'
  | 'thumbnailUrl'
  | 'imageUrl'
  | 'footerText'
  | 'footerIconUrl';

/**
 * Applies a string renderer to every text/URL field of an embed payload.
 * Used by Welcome/Tags/Scheduler before handing data to discord.js.
 * The second argument names the field so callers can e.g. avoid Discord
 * mentions in title/author/footer (Discord does not resolve them there).
 */
export function mapEmbedTextFields(
  embed: WelcomeEmbed,
  render: (value: string, field: EmbedTextField) => string
): WelcomeEmbed {
  const map = (value: string | undefined, field: EmbedTextField): string | undefined => {
    if (!value?.trim()) {
      return undefined;
    }
    return render(value, field);
  };
  return {
    title: map(embed.title, 'title'),
    description: map(embed.description, 'description'),
    url: map(embed.url, 'url'),
    color: embed.color,
    authorName: map(embed.authorName, 'authorName'),
    authorIconUrl: map(embed.authorIconUrl, 'authorIconUrl'),
    authorUrl: map(embed.authorUrl, 'authorUrl'),
    thumbnailUrl: map(embed.thumbnailUrl, 'thumbnailUrl'),
    imageUrl: map(embed.imageUrl, 'imageUrl'),
    footerText: map(embed.footerText, 'footerText'),
    footerIconUrl: map(embed.footerIconUrl, 'footerIconUrl'),
    timestamp: embed.timestamp
  };
}

export const VerificationModeSchema = z.enum(['BUTTON', 'CAPTCHA']);
export type VerificationMode = z.infer<typeof VerificationModeSchema>;

export const VerificationFailActionSchema = z.enum(['KICK', 'BAN', 'NONE']);
export type VerificationFailAction = z.infer<typeof VerificationFailActionSchema>;

export const CaptchaProviderSchema = z.enum([
  'MATH',
  'RECAPTCHA_V2',
  'RECAPTCHA_V3',
  'HCAPTCHA',
  'TURNSTILE'
]);
export type CaptchaProvider = z.infer<typeof CaptchaProviderSchema>;

export const VerificationSetupSchema = z.object({
  channel: z.string().min(1),
  verifiedRole: z.string().min(1),
  unverifiedRole: z.string().optional(),
  mode: VerificationModeSchema.default('BUTTON'),
  captchaProvider: CaptchaProviderSchema.default('MATH'),
  minAccountAgeDays: z.number().int().nonnegative().default(0),
  failAction: VerificationFailActionSchema.default('KICK'),
  altDetectionEnabled: z.boolean().default(false),
  altBlockOnMatch: z.boolean().default(true),
  altIpWindowDays: z.number().int().min(1).max(365).default(30),
  altInviteWindowHours: z.number().int().min(1).max(720).default(48),
  altMaxAccountsPerInvite: z.number().int().min(1).max(50).default(3)
});
export type VerificationSetup = z.infer<typeof VerificationSetupSchema>;

export const WELCOME_PLACEHOLDERS = [
  '{user}',
  '{user.name}',
  '{user.tag}',
  '{user.id}',
  '{server}',
  '{memberCount}'
] as const;

/**
 * Expands dashboard-friendly channel mentions `["123…"]` into Discord's
 * native `<#123…>` mention format. Raw `<#id>` mentions are left untouched.
 */
export function expandBracketChannelMentions(template: string): string {
  return template.replace(/\["(\d{17,20})"\]/g, '<#$1>');
}

export function applyWelcomePlaceholders(
  template: string,
  vars: Record<string, string | number>
): string {
  const withVars = Object.entries(vars).reduce(
    (acc, [name, value]) => acc.replaceAll(`{${name}}`, String(value)),
    template
  );
  return expandBracketChannelMentions(withVars);
}

export function countEmojis(content: string): number {
  const emojiRegex =
    /(\p{Extended_Pictographic}|\p{Emoji_Presentation}|<a?:\w+:\d+>)/gu;
  return content.match(emojiRegex)?.length ?? 0;
}

export function capsRatio(content: string): number {
  const letters = content.replace(/[^a-zA-Z]/g, '');
  if (letters.length === 0) {
    return 0;
  }
  const caps = letters.replace(/[^A-Z]/g, '').length;
  return (caps / letters.length) * 100;
}

export function hasZalgo(text: string): boolean {
  return /[\u0300-\u036f\u0489]/.test(text);
}

export function extractUrls(content: string): string[] {
  const urlRegex = /https?:\/\/[^\s<>]+/gi;
  return content.match(urlRegex) ?? [];
}

export function isDiscordInvite(content: string): boolean {
  return /(?:https?:\/\/)?(?:www\.)?(?:discord\.(?:gg|io|me|li)|discordapp\.com\/invite|discord\.com\/invite)\/[^\s]+/i.test(
    content
  );
}

export function normalizeHost(url: string): string | null {
  try {
    return new URL(url).hostname.replace(/^www\./, '').toLowerCase();
  } catch {
    return null;
  }
}
