import { z } from 'zod';
import {
  componentsV2HasContent,
  MessageComponentsV2Schema
} from './components-v2.js';
import {
  embedHasContent,
  LeaveMessageTypeSchema,
  WelcomeEmbedSchema,
  AutoModRuleTypeSchema,
  AutoModActionSchema,
  AutoModExceptionsSchema,
  AutoModThresholdSchema,
  AutoModWordListSchema
} from './phase2.js';
import {
  SelfRoleBehaviorSchema,
  SelfRoleEntrySchema,
  SelfRoleModeSchema,
  SuggestionStatusSchema,
  TagResponseTypeSchema,
  TicketModeSchema,
  TicketPrioritySchema,
  TicketStatusSchema
} from './phase4.js';
import { SocialFeedTypeSchema } from './phase5.js';

/** Dashboard embed payload shared by Welcome, Tags, Scheduler, etc. */
export const DashboardEmbedSchema = WelcomeEmbedSchema;
export type DashboardEmbed = z.infer<typeof DashboardEmbedSchema>;

/**
 * Discord snowflake ID: 17-20 digit numeric string.
 */
export const SnowflakeSchema = z.string().regex(/^\d{17,20}$/, 'Invalid Discord ID');

/**
 * Optional snowflake field for forms: accepts an empty string (treated as
 * "unset"/cleared) or a valid snowflake. Kept as a plain string (rather than
 * transformed to `undefined`) so that `JSON.stringify` does not silently
 * drop the field when a user clears it in the dashboard - the API layer
 * maps `''` to `null` when persisting.
 */
export const OptionalSnowflakeSchema = z.union([z.literal(''), SnowflakeSchema]);

function nonEmptyPatch<T extends z.ZodRawShape>(schema: z.ZodObject<T>) {
  return schema.partial().refine((value) => Object.keys(value).length > 0, {
    message: 'At least one field is required'
  });
}

// ---------------------------------------------------------------------------
// Moderation
// ---------------------------------------------------------------------------

export const EscalationActionSchema = z.enum(['TIMEOUT', 'KICK', 'BAN']);
export type EscalationAction = z.infer<typeof EscalationActionSchema>;

export const EscalationRuleDashboardSchema = z.object({
  id: z.string().optional(),
  warnCount: z.number().int().positive(),
  action: EscalationActionSchema,
  durationMs: z.number().int().positive().nullable().optional(),
  reason: z.string().max(500).nullable().optional(),
  enabled: z.boolean().default(true)
});
export type EscalationRuleDashboard = z.infer<typeof EscalationRuleDashboardSchema>;

export const ModerationDashboardSchema = z.object({
  moderationEnabled: z.boolean(),
  escalations: z.array(EscalationRuleDashboardSchema)
});
export type ModerationDashboard = z.infer<typeof ModerationDashboardSchema>;

export const ModerationDashboardPatchSchema = z
  .object({
    moderationEnabled: z.boolean().optional(),
    escalations: z.array(EscalationRuleDashboardSchema).optional()
  })
  .refine((value) => Object.keys(value).length > 0, { message: 'At least one field is required' });
export type ModerationDashboardPatch = z.infer<typeof ModerationDashboardPatchSchema>;

export const CaseDashboardSchema = z.object({
  id: z.string(),
  caseNumber: z.number().int(),
  targetUserId: z.string(),
  moderatorId: z.string(),
  action: z.string(),
  reason: z.string().nullable(),
  createdAt: z.string(),
  deletedAt: z.string().nullable()
});
export type CaseDashboard = z.infer<typeof CaseDashboardSchema>;

export const CaseListQuerySchema = z.object({
  search: z.string().trim().max(100).optional(),
  action: z.string().trim().max(50).optional(),
  limit: z.coerce.number().int().positive().max(100).default(25)
});
export type CaseListQuery = z.infer<typeof CaseListQuerySchema>;

export const WarningDashboardSchema = z.object({
  id: z.string(),
  userId: z.string(),
  moderatorId: z.string(),
  reason: z.string().nullable(),
  caseId: z.string().nullable(),
  createdAt: z.string()
});
export type WarningDashboard = z.infer<typeof WarningDashboardSchema>;

export const WarningListQuerySchema = z.object({
  userId: SnowflakeSchema.optional()
});
export type WarningListQuery = z.infer<typeof WarningListQuerySchema>;

// ---------------------------------------------------------------------------
// AutoMod
// ---------------------------------------------------------------------------

export const AntiRaidActionSchema = z.enum(['LOCKDOWN', 'VERIFY']);

export const AutomodRuleDashboardSchema = z.object({
  ruleType: AutoModRuleTypeSchema,
  enabled: z.boolean(),
  action: AutoModActionSchema,
  durationMs: z.number().int().positive().nullable().optional(),
  threshold: AutoModThresholdSchema.default({}),
  exceptions: AutoModExceptionsSchema.default({ roleIds: [], channelIds: [] }),
  wordList: AutoModWordListSchema.default({ words: [], regexPatterns: [] })
});
export type AutomodRuleDashboard = z.infer<typeof AutomodRuleDashboardSchema>;

export const AutomodConfigDashboardSchema = z.object({
  enabled: z.boolean(),
  antiRaidEnabled: z.boolean(),
  antiRaidJoinThreshold: z.number().int().positive(),
  antiRaidWindowSeconds: z.number().int().positive(),
  antiRaidAction: AntiRaidActionSchema,
  antiNukeEnabled: z.boolean(),
  antiNukeBanThreshold: z.number().int().positive(),
  antiNukeChannelThreshold: z.number().int().positive(),
  antiNukeWindowSeconds: z.number().int().positive(),
  lockdownActive: z.boolean(),
  rules: z.array(AutomodRuleDashboardSchema)
});
export type AutomodConfigDashboard = z.infer<typeof AutomodConfigDashboardSchema>;

export const AutomodConfigDashboardPatchSchema = nonEmptyPatch(AutomodConfigDashboardSchema);
export type AutomodConfigDashboardPatch = z.infer<typeof AutomodConfigDashboardPatchSchema>;

export const CaseUpdateDashboardSchema = z.object({
  reason: z.string().trim().min(1).max(1000)
});
export type CaseUpdateDashboard = z.infer<typeof CaseUpdateDashboardSchema>;

/** Returns true when every regex pattern compiles. */
export function validateAutomodRegexPatterns(patterns: string[]): string | null {
  for (const pattern of patterns) {
    try {
      RegExp(pattern, 'iu');
    } catch {
      return pattern;
    }
  }
  return null;
}
// ---------------------------------------------------------------------------
// Logging
// ---------------------------------------------------------------------------

export const LogEventTypeDashboardSchema = 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 LogEventTypeDashboard = z.infer<typeof LogEventTypeDashboardSchema>;

export const LogChannelMappingSchema = z.object({
  eventType: LogEventTypeDashboardSchema,
  channelId: SnowflakeSchema
});
export type LogChannelMapping = z.infer<typeof LogChannelMappingSchema>;

export const LoggingConfigDashboardSchema = z.object({
  enabled: z.boolean(),
  ignoreBots: z.boolean(),
  ignoredChannelIds: z.array(z.string()),
  ignoredRoleIds: z.array(z.string()),
  retentionDays: z.number().int().min(0).max(3650),
  logChannels: z.array(LogChannelMappingSchema)
});
export type LoggingConfigDashboard = z.infer<typeof LoggingConfigDashboardSchema>;

export const LoggingConfigDashboardPatchSchema = z
  .object({
    enabled: z.boolean().optional(),
    ignoreBots: z.boolean().optional(),
    ignoredChannelIds: z.array(z.string()).optional(),
    ignoredRoleIds: z.array(z.string()).optional(),
    retentionDays: z.number().int().min(0).max(3650).optional(),
    logChannels: z.array(LogChannelMappingSchema).optional()
  })
  .refine((value) => Object.keys(value).length > 0, { message: 'At least one field is required' });
export type LoggingConfigDashboardPatch = z.infer<typeof LoggingConfigDashboardPatchSchema>;

// ---------------------------------------------------------------------------
// Welcome & Leave
// ---------------------------------------------------------------------------

export const WelcomeMessageTypeDashboardSchema = z.enum([
  'TEXT',
  'EMBED',
  'IMAGE',
  'COMPONENTS_V2'
]);
export const LeaveMessageTypeDashboardSchema = LeaveMessageTypeSchema;

export const WelcomeConfigDashboardSchema = z.object({
  welcomeEnabled: z.boolean(),
  leaveEnabled: z.boolean(),
  welcomeChannelId: OptionalSnowflakeSchema,
  leaveChannelId: OptionalSnowflakeSchema,
  welcomeType: WelcomeMessageTypeDashboardSchema,
  welcomeContent: z.string().max(2000).nullable().optional(),
  welcomeEmbed: DashboardEmbedSchema.nullable().optional(),
  welcomeComponents: MessageComponentsV2Schema.nullable().optional(),
  leaveType: LeaveMessageTypeDashboardSchema.default('TEXT'),
  leaveContent: z.string().max(2000).nullable().optional(),
  leaveEmbed: DashboardEmbedSchema.nullable().optional(),
  leaveComponents: MessageComponentsV2Schema.nullable().optional(),
  welcomeDmEnabled: z.boolean(),
  welcomeDmContent: z.string().max(2000).nullable().optional(),
  userAutoroleIds: z.array(z.string()),
  botAutoroleIds: z.array(z.string()),
  imageTitle: z.string().max(200).nullable().optional(),
  imageSubtitle: z.string().max(200).nullable().optional()
});
export type WelcomeConfigDashboard = z.infer<typeof WelcomeConfigDashboardSchema>;

export const WelcomeConfigDashboardPatchSchema = nonEmptyPatch(WelcomeConfigDashboardSchema);
export type WelcomeConfigDashboardPatch = z.infer<typeof WelcomeConfigDashboardPatchSchema>;

// ---------------------------------------------------------------------------
// Verification
// ---------------------------------------------------------------------------

export const VerificationModeDashboardSchema = z.enum(['BUTTON', 'CAPTCHA']);
export const VerificationFailActionDashboardSchema = z.enum(['KICK', 'BAN', 'NONE']);
export const CaptchaProviderDashboardSchema = z.enum([
  'MATH',
  'RECAPTCHA_V2',
  'RECAPTCHA_V3',
  'HCAPTCHA',
  'TURNSTILE'
]);

export const VerificationConfigDashboardSchema = z.object({
  enabled: z.boolean(),
  channelId: OptionalSnowflakeSchema,
  verifiedRoleId: OptionalSnowflakeSchema,
  unverifiedRoleId: OptionalSnowflakeSchema,
  mode: VerificationModeDashboardSchema,
  captchaProvider: CaptchaProviderDashboardSchema,
  minAccountAgeDays: z.number().int().nonnegative(),
  failAction: VerificationFailActionDashboardSchema,
  altDetectionEnabled: z.boolean(),
  altBlockOnMatch: z.boolean(),
  altIpWindowDays: z.number().int().min(1).max(365),
  altInviteWindowHours: z.number().int().min(1).max(720),
  altMaxAccountsPerInvite: z.number().int().min(1).max(50)
});
export type VerificationConfigDashboard = z.infer<typeof VerificationConfigDashboardSchema>;

export const VerificationConfigDashboardPatchSchema = nonEmptyPatch(VerificationConfigDashboardSchema);
export type VerificationConfigDashboardPatch = z.infer<typeof VerificationConfigDashboardPatchSchema>;

// ---------------------------------------------------------------------------
// Leveling
// ---------------------------------------------------------------------------

export const LevelUpModeDashboardSchema = z.enum(['CHANNEL', 'DM', 'OFF']);

export const LevelRewardDashboardSchema = z.object({
  level: z.number().int().min(1).max(10_000),
  roleId: SnowflakeSchema
});
export type LevelRewardDashboard = z.infer<typeof LevelRewardDashboardSchema>;

export const LevelingConfigDashboardSchema = z.object({
  enabled: z.boolean(),
  textXpMin: z.number().int().positive(),
  textXpMax: z.number().int().positive(),
  textCooldownSeconds: z.number().int().positive(),
  voiceXpPerMinute: z.number().int().nonnegative(),
  noXpChannelIds: z.array(z.string()),
  noXpRoleIds: z.array(z.string()),
  roleMultipliers: z.record(z.string(), z.number().positive()),
  channelMultipliers: z.record(z.string(), z.number().positive()),
  levelUpMode: LevelUpModeDashboardSchema,
  levelUpChannelId: OptionalSnowflakeSchema,
  levelUpMessage: z.string().max(500).nullable().optional(),
  stackRewards: z.boolean(),
  rewards: z.array(LevelRewardDashboardSchema).max(100),
  cardAccentColor: z.string().regex(/^#[0-9a-fA-F]{6}$/),
  cardBackgroundColor: z.string().regex(/^#[0-9a-fA-F]{6}$/)
});
export type LevelingConfigDashboard = z.infer<typeof LevelingConfigDashboardSchema>;

export const LevelingConfigDashboardPatchSchema = nonEmptyPatch(LevelingConfigDashboardSchema);
export type LevelingConfigDashboardPatch = z.infer<typeof LevelingConfigDashboardPatchSchema>;

/** Returns true when every reward level is unique. */
export function hasUniqueRewardLevels(rewards: LevelRewardDashboard[]): boolean {
  const levels = new Set<number>();
  for (const reward of rewards) {
    if (levels.has(reward.level)) {
      return false;
    }
    levels.add(reward.level);
  }
  return true;
}

// ---------------------------------------------------------------------------
// Economy
// ---------------------------------------------------------------------------

export const EconomyConfigDashboardSchema = z.object({
  enabled: z.boolean(),
  currencyName: z.string().min(1).max(32),
  currencySymbol: z.string().min(1).max(8),
  dailyAmount: z.number().int().nonnegative(),
  weeklyAmount: z.number().int().nonnegative(),
  workMin: z.number().int().nonnegative(),
  workMax: z.number().int().nonnegative(),
  workCooldownSeconds: z.number().int().positive(),
  startingBalance: z.number().int().nonnegative()
});
export type EconomyConfigDashboard = z.infer<typeof EconomyConfigDashboardSchema>;

export const EconomyConfigDashboardPatchSchema = nonEmptyPatch(EconomyConfigDashboardSchema);
export type EconomyConfigDashboardPatch = z.infer<typeof EconomyConfigDashboardPatchSchema>;

// ---------------------------------------------------------------------------
// Fun
// ---------------------------------------------------------------------------

export const FunConfigDashboardSchema = z.object({
  enabled: z.boolean(),
  mediaEnabled: z.boolean()
});
export type FunConfigDashboard = z.infer<typeof FunConfigDashboardSchema>;

export const FunConfigDashboardPatchSchema = nonEmptyPatch(FunConfigDashboardSchema);
export type FunConfigDashboardPatch = z.infer<typeof FunConfigDashboardPatchSchema>;

// ---------------------------------------------------------------------------
// Tickets
// ---------------------------------------------------------------------------

export const TicketConfigDashboardSchema = z.object({
  enabled: z.boolean(),
  mode: TicketModeSchema,
  categoryId: OptionalSnowflakeSchema,
  logChannelId: OptionalSnowflakeSchema,
  panelChannelId: OptionalSnowflakeSchema,
  transcriptDm: z.boolean(),
  inactivityHours: z.number().int().positive(),
  ratingEnabled: z.boolean()
});
export type TicketConfigDashboard = z.infer<typeof TicketConfigDashboardSchema>;

export const TicketConfigDashboardPatchSchema = nonEmptyPatch(TicketConfigDashboardSchema);
export type TicketConfigDashboardPatch = z.infer<typeof TicketConfigDashboardPatchSchema>;

export const TicketCategoryDashboardSchema = z.object({
  id: z.string().optional(),
  name: z.string().min(1).max(100),
  description: z.string().max(500).nullable().optional(),
  emoji: z.string().max(80).nullable().optional(),
  supportRoleIds: z.array(SnowflakeSchema)
});
export type TicketCategoryDashboard = z.infer<typeof TicketCategoryDashboardSchema>;

export const TicketCategoryDashboardCreateSchema = TicketCategoryDashboardSchema.omit({ id: true });
export type TicketCategoryDashboardCreate = z.infer<typeof TicketCategoryDashboardCreateSchema>;

export const TicketCategoryDashboardUpdateSchema = nonEmptyPatch(TicketCategoryDashboardCreateSchema);
export type TicketCategoryDashboardUpdate = z.infer<typeof TicketCategoryDashboardUpdateSchema>;

export const TicketDashboardSchema = z.object({
  id: z.string(),
  openerId: z.string(),
  claimedById: z.string().nullable(),
  channelId: z.string().nullable(),
  threadId: z.string().nullable(),
  status: TicketStatusSchema,
  priority: TicketPrioritySchema,
  subject: z.string().nullable(),
  categoryId: z.string().nullable(),
  categoryName: z.string().nullable(),
  lastActivityAt: z.string(),
  createdAt: z.string()
});
export type TicketDashboard = z.infer<typeof TicketDashboardSchema>;

export const TicketDashboardActionSchema = z.discriminatedUnion('action', [
  z.object({
    action: z.literal('close'),
    reason: z.string().max(500).optional()
  }),
  z.object({
    action: z.literal('claim')
  }),
  z.object({
    action: z.literal('priority'),
    priority: TicketPrioritySchema
  })
]);
export type TicketDashboardAction = z.infer<typeof TicketDashboardActionSchema>;

// ---------------------------------------------------------------------------
// Giveaways
// ---------------------------------------------------------------------------

export const GiveawayDashboardSchema = z.object({
  id: z.string(),
  guildId: z.string(),
  channelId: z.string(),
  messageId: z.string().nullable(),
  prize: z.string(),
  winnerCount: z.number().int(),
  endsAt: z.string(),
  requiredRoleId: z.string().nullable(),
  requiredLevel: z.number().int().nullable(),
  requiredMemberDays: z.number().int().nullable(),
  paused: z.boolean(),
  endedAt: z.string().nullable(),
  winners: z.array(z.string()),
  entrants: z.array(z.string()),
  hostId: z.string(),
  createdAt: z.string()
});
export type GiveawayDashboard = z.infer<typeof GiveawayDashboardSchema>;

export const GiveawayCreateDashboardSchema = z.object({
  channelId: SnowflakeSchema,
  prize: z.string().min(1).max(200),
  winnerCount: z.number().int().positive().max(50),
  endsAt: z.string().refine((value) => !Number.isNaN(Date.parse(value)), 'Invalid date'),
  requiredRoleId: OptionalSnowflakeSchema.optional(),
  requiredLevel: z.number().int().nonnegative().nullable().optional(),
  requiredMemberDays: z.number().int().nonnegative().nullable().optional()
});
export type GiveawayCreateDashboard = z.infer<typeof GiveawayCreateDashboardSchema>;

export const GiveawayActionSchema = z.enum(['end', 'pause', 'unpause']);
export type GiveawayAction = z.infer<typeof GiveawayActionSchema>;

// ---------------------------------------------------------------------------
// Self Roles
// ---------------------------------------------------------------------------

export const SelfRolePanelDashboardSchema = z.object({
  id: z.string().optional(),
  channelId: SnowflakeSchema,
  title: z.string().min(1).max(200),
  description: z.string().max(1000).nullable().optional(),
  mode: SelfRoleModeSchema,
  behavior: SelfRoleBehaviorSchema,
  roles: z.array(SelfRoleEntrySchema).min(1),
  expiresAt: z.string().nullable().optional()
});
export type SelfRolePanelDashboard = z.infer<typeof SelfRolePanelDashboardSchema>;

export const SelfRolePanelDashboardCreateSchema = SelfRolePanelDashboardSchema.omit({ id: true });
export type SelfRolePanelDashboardCreate = z.infer<typeof SelfRolePanelDashboardCreateSchema>;

export const SelfRolePanelDashboardUpdateSchema = nonEmptyPatch(SelfRolePanelDashboardCreateSchema);
export type SelfRolePanelDashboardUpdate = z.infer<typeof SelfRolePanelDashboardUpdateSchema>;

// ---------------------------------------------------------------------------
// Tags
// ---------------------------------------------------------------------------

export const TagDashboardSchema = z.object({
  id: z.string().optional(),
  name: z.string().min(1).max(50),
  content: z.string().max(2000).nullable().optional(),
  embed: DashboardEmbedSchema.nullable().optional(),
  components: MessageComponentsV2Schema.nullable().optional(),
  responseType: TagResponseTypeSchema,
  triggerWord: z.string().max(100).nullable().optional(),
  /** Seconds between auto-responder /tag replies; 0 disables. Default 15. */
  cooldownSeconds: z.number().int().min(0).max(3600).default(15),
  allowedRoleIds: z.array(z.string()),
  allowedChannelIds: z.array(z.string())
});
export type TagDashboard = z.infer<typeof TagDashboardSchema>;

export const TagDashboardCreateSchema = TagDashboardSchema.omit({ id: true })
  .refine(
    (value) =>
      value.responseType !== 'EMBED' ||
      embedHasContent(value.embed) ||
      value.embed?.color !== undefined,
    { message: 'Embed content is required for EMBED tags', path: ['embed'] }
  )
  .refine(
    (value) =>
      value.responseType !== 'COMPONENTS_V2' || componentsV2HasContent(value.components),
    { message: 'Components are required for COMPONENTS_V2 tags', path: ['components'] }
  )
  .refine(
    (value) =>
      value.responseType !== 'COMPONENTS_V2' ||
      (!embedHasContent(value.embed) && value.embed?.color === undefined),
    { message: 'Embed and Components V2 are mutually exclusive', path: ['embed'] }
  );
export type TagDashboardCreate = z.infer<typeof TagDashboardCreateSchema>;

export const TagDashboardUpdateSchema = nonEmptyPatch(TagDashboardSchema.omit({ id: true }));
export type TagDashboardUpdate = z.infer<typeof TagDashboardUpdateSchema>;

// ---------------------------------------------------------------------------
// Starboard
// ---------------------------------------------------------------------------

export const StarboardConfigDashboardSchema = z
  .object({
    enabled: z.boolean(),
    channelId: OptionalSnowflakeSchema,
    emoji: z.string().min(1).max(80),
    threshold: z.number().int().min(1).max(100),
    allowSelfStar: z.boolean(),
    ignoredChannelIds: z.array(SnowflakeSchema)
  })
  .superRefine((value, ctx) => {
    if (value.enabled && !value.channelId) {
      ctx.addIssue({
        code: z.ZodIssueCode.custom,
        message: 'channelId is required when starboard is enabled',
        path: ['channelId']
      });
    }
  });
export type StarboardConfigDashboard = z.infer<typeof StarboardConfigDashboardSchema>;

export const StarboardConfigDashboardPatchSchema = nonEmptyPatch(
  z.object({
    enabled: z.boolean(),
    channelId: OptionalSnowflakeSchema,
    emoji: z.string().min(1).max(80),
    threshold: z.number().int().min(1).max(100),
    allowSelfStar: z.boolean(),
    ignoredChannelIds: z.array(SnowflakeSchema)
  })
).superRefine((value, ctx) => {
  if (value.enabled === true && value.channelId === '') {
    ctx.addIssue({
      code: z.ZodIssueCode.custom,
      message: 'channelId is required when starboard is enabled',
      path: ['channelId']
    });
  }
});
export type StarboardConfigDashboardPatch = z.infer<typeof StarboardConfigDashboardPatchSchema>;

// ---------------------------------------------------------------------------
// Suggestions
// ---------------------------------------------------------------------------

export const SuggestionConfigDashboardSchema = z.object({
  enabled: z.boolean(),
  openChannelId: OptionalSnowflakeSchema,
  approvedChannelId: OptionalSnowflakeSchema,
  deniedChannelId: OptionalSnowflakeSchema,
  createThread: z.boolean()
});
export type SuggestionConfigDashboard = z.infer<typeof SuggestionConfigDashboardSchema>;

export const SuggestionConfigDashboardPatchSchema = nonEmptyPatch(SuggestionConfigDashboardSchema);
export type SuggestionConfigDashboardPatch = z.infer<typeof SuggestionConfigDashboardPatchSchema>;

export const SuggestionDashboardSchema = z.object({
  id: z.string(),
  authorId: z.string(),
  content: z.string(),
  status: SuggestionStatusSchema,
  upvotes: z.number().int(),
  downvotes: z.number().int(),
  staffReason: z.string().nullable(),
  createdAt: z.string()
});
export type SuggestionDashboard = z.infer<typeof SuggestionDashboardSchema>;

export const SuggestionListQuerySchema = z.object({
  status: SuggestionStatusSchema.optional(),
  limit: z.coerce.number().int().positive().max(100).default(25)
});
export type SuggestionListQuery = z.infer<typeof SuggestionListQuerySchema>;

export const SuggestionActionSchema = z.object({
  status: SuggestionStatusSchema,
  staffReason: z.string().max(500).nullable().optional()
});
export type SuggestionAction = z.infer<typeof SuggestionActionSchema>;

// ---------------------------------------------------------------------------
// Birthdays
// ---------------------------------------------------------------------------

export const BirthdayConfigDashboardSchema = z.object({
  enabled: z.boolean(),
  channelId: OptionalSnowflakeSchema,
  roleId: OptionalSnowflakeSchema,
  message: z.string().max(500).nullable().optional(),
  timezone: z.string().min(1).max(64)
});
export type BirthdayConfigDashboard = z.infer<typeof BirthdayConfigDashboardSchema>;

export const BirthdayConfigDashboardPatchSchema = nonEmptyPatch(BirthdayConfigDashboardSchema);
export type BirthdayConfigDashboardPatch = z.infer<typeof BirthdayConfigDashboardPatchSchema>;

// ---------------------------------------------------------------------------
// Temp-Voice
// ---------------------------------------------------------------------------

export const TempVoiceConfigDashboardSchema = z.object({
  enabled: z.boolean(),
  hubChannelId: OptionalSnowflakeSchema,
  categoryId: OptionalSnowflakeSchema,
  defaultName: z.string().min(1).max(100),
  defaultLimit: z.number().int().nonnegative().max(99)
});
export type TempVoiceConfigDashboard = z.infer<typeof TempVoiceConfigDashboardSchema>;

export const TempVoiceConfigDashboardPatchSchema = nonEmptyPatch(TempVoiceConfigDashboardSchema);
export type TempVoiceConfigDashboardPatch = z.infer<typeof TempVoiceConfigDashboardPatchSchema>;

// ---------------------------------------------------------------------------
// Server Stats
// ---------------------------------------------------------------------------

export const StatsConfigDashboardSchema = z.object({
  enabled: z.boolean(),
  membersChannelId: OptionalSnowflakeSchema,
  onlineChannelId: OptionalSnowflakeSchema,
  boostsChannelId: OptionalSnowflakeSchema,
  membersTemplate: z.string().min(1).max(100),
  onlineTemplate: z.string().min(1).max(100),
  boostsTemplate: z.string().min(1).max(100)
});
export type StatsConfigDashboard = z.infer<typeof StatsConfigDashboardSchema>;

export const StatsConfigDashboardPatchSchema = nonEmptyPatch(StatsConfigDashboardSchema);
export type StatsConfigDashboardPatch = z.infer<typeof StatsConfigDashboardPatchSchema>;

export const ActivityStatSummarySchema = z.object({
  messageCount: z.number().int().nonnegative(),
  voiceMinutes: z.number().int().nonnegative(),
  activeUserCount: z.number().int().nonnegative()
});
export type ActivityStatSummary = z.infer<typeof ActivityStatSummarySchema>;

// ---------------------------------------------------------------------------
// Social Feeds
// ---------------------------------------------------------------------------

export const SocialFeedDashboardSchema = z.object({
  id: z.string().optional(),
  type: SocialFeedTypeSchema,
  sourceId: z.string().min(1).max(200),
  channelId: SnowflakeSchema,
  rolePingId: OptionalSnowflakeSchema.optional(),
  template: z.string().max(500).nullable().optional(),
  enabled: z.boolean()
});
export type SocialFeedDashboard = z.infer<typeof SocialFeedDashboardSchema>;

export const SocialFeedDashboardCreateSchema = SocialFeedDashboardSchema.omit({ id: true });
export type SocialFeedDashboardCreate = z.infer<typeof SocialFeedDashboardCreateSchema>;

export const SocialFeedDashboardUpdateSchema = nonEmptyPatch(SocialFeedDashboardCreateSchema);
export type SocialFeedDashboardUpdate = z.infer<typeof SocialFeedDashboardUpdateSchema>;

// ---------------------------------------------------------------------------
// Scheduler
// ---------------------------------------------------------------------------

export const ScheduledMessageDashboardSchema = z.object({
  id: z.string(),
  channelId: z.string(),
  creatorId: z.string(),
  content: z.string().nullable(),
  embed: DashboardEmbedSchema.nullable().optional(),
  components: MessageComponentsV2Schema.nullable().optional(),
  rolePingId: z.string().nullable(),
  cron: z.string().nullable(),
  runAt: z.string().nullable(),
  enabled: z.boolean(),
  createdAt: z.string()
});
export type ScheduledMessageDashboard = z.infer<typeof ScheduledMessageDashboardSchema>;

export const ScheduledMessageDashboardCreateSchema = z
  .object({
    channelId: SnowflakeSchema,
    content: z.string().max(2000).nullable().optional(),
    embed: DashboardEmbedSchema.nullable().optional(),
    components: MessageComponentsV2Schema.nullable().optional(),
    rolePingId: OptionalSnowflakeSchema.optional(),
    cron: z.string().min(1).max(100).nullable().optional(),
    runAt: z
      .string()
      .refine((value) => !Number.isNaN(Date.parse(value)), 'Invalid date')
      .nullable()
      .optional()
  })
  .refine((value) => Boolean(value.cron?.trim()) || Boolean(value.runAt), {
    message: 'Either cron or runAt is required'
  })
  .refine(
    (value) => {
      const hasComponents = componentsV2HasContent(value.components);
      const hasEmbed = embedHasContent(value.embed) || value.embed?.color !== undefined;
      if (hasComponents && hasEmbed) {
        return false;
      }
      if (hasComponents) {
        return true;
      }
      return Boolean(value.content?.trim()) || hasEmbed;
    },
    { message: 'Content, embed, or Components V2 is required (embed and V2 are exclusive)' }
  );
export type ScheduledMessageDashboardCreate = z.infer<typeof ScheduledMessageDashboardCreateSchema>;

// ---------------------------------------------------------------------------
// Dashboard message sender (Embed | Components V2)
// ---------------------------------------------------------------------------

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

export const DashboardMessageSendSchema = z
  .object({
    channelId: SnowflakeSchema,
    mode: DashboardMessageModeSchema,
    embed: DashboardEmbedSchema.nullable().optional(),
    components: MessageComponentsV2Schema.nullable().optional()
  })
  .superRefine((value, ctx) => {
    if (value.mode === 'EMBED') {
      if (!embedHasContent(value.embed) && value.embed?.color === undefined) {
        ctx.addIssue({
          code: z.ZodIssueCode.custom,
          message: 'Embed content is required',
          path: ['embed']
        });
      }
      if (componentsV2HasContent(value.components)) {
        ctx.addIssue({
          code: z.ZodIssueCode.custom,
          message: 'Components V2 not allowed in EMBED mode',
          path: ['components']
        });
      }
    } else if (!componentsV2HasContent(value.components)) {
      ctx.addIssue({
        code: z.ZodIssueCode.custom,
        message: 'Components are required for COMPONENTS_V2 mode',
        path: ['components']
      });
    }
  });
export type DashboardMessageSend = z.infer<typeof DashboardMessageSendSchema>;

export const DashboardMessageSendResultSchema = z.object({
  bindingId: z.string().nullable(),
  messageId: z.string().nullable(),
  channelId: z.string(),
  confirmed: z.boolean()
});
export type DashboardMessageSendResult = z.infer<typeof DashboardMessageSendResultSchema>;

// ---------------------------------------------------------------------------
// Guild Backups
// ---------------------------------------------------------------------------

export const GuildBackupDashboardSchema = z.object({
  id: z.string(),
  name: z.string(),
  createdById: z.string(),
  createdAt: z.string(),
  roleCount: z.number().int().nonnegative(),
  channelCount: z.number().int().nonnegative()
});
export type GuildBackupDashboard = z.infer<typeof GuildBackupDashboardSchema>;

export const GuildBackupCreateDashboardSchema = z.object({
  name: z.string().min(1).max(100)
});
export type GuildBackupCreateDashboard = z.infer<typeof GuildBackupCreateDashboardSchema>;

// ---------------------------------------------------------------------------
// Command Overrides
// ---------------------------------------------------------------------------

export const CommandOverrideDashboardSchema = z.object({
  commandName: z.string().min(1).max(100),
  enabled: z.boolean(),
  allowedRoleIds: z.array(SnowflakeSchema),
  deniedRoleIds: z.array(SnowflakeSchema),
  allowedChannelIds: z.array(SnowflakeSchema),
  deniedChannelIds: z.array(SnowflakeSchema),
  cooldownSeconds: z.number().int().nonnegative().nullable()
});
export type CommandOverrideDashboard = z.infer<typeof CommandOverrideDashboardSchema>;

export const CommandOverrideDashboardUpsertSchema = z.object({
  commandName: z.string().min(1).max(100),
  enabled: z.boolean().default(true),
  allowedRoleIds: z.array(SnowflakeSchema).default([]),
  deniedRoleIds: z.array(SnowflakeSchema).default([]),
  allowedChannelIds: z.array(SnowflakeSchema).default([]),
  deniedChannelIds: z.array(SnowflakeSchema).default([]),
  cooldownSeconds: z.number().int().nonnegative().nullable().optional()
});
export type CommandOverrideDashboardUpsert = z.infer<typeof CommandOverrideDashboardUpsertSchema>;

export const CommandGlobalRulesSchema = z.object({
  allowedChannelIds: z.array(SnowflakeSchema),
  deniedChannelIds: z.array(SnowflakeSchema)
});
export type CommandGlobalRules = z.infer<typeof CommandGlobalRulesSchema>;

export const CommandGlobalRulesPatchSchema = CommandGlobalRulesSchema.partial().refine(
  (value) => Object.keys(value).length > 0,
  { message: 'At least one field is required' }
);
export type CommandGlobalRulesPatch = z.infer<typeof CommandGlobalRulesPatchSchema>;

export const DiscordChannelOptionSchema = z.object({
  id: SnowflakeSchema,
  name: z.string(),
  type: z.number().int(),
  parentId: SnowflakeSchema.nullable().optional()
});
export type DiscordChannelOption = z.infer<typeof DiscordChannelOptionSchema>;

export const DiscordRoleOptionSchema = z.object({
  id: SnowflakeSchema,
  name: z.string(),
  color: z.number().int(),
  position: z.number().int()
});
export type DiscordRoleOption = z.infer<typeof DiscordRoleOptionSchema>;

export const DiscordEmojiOptionSchema = z.object({
  id: SnowflakeSchema,
  name: z.string(),
  animated: z.boolean(),
  url: z.string().url()
});
export type DiscordEmojiOption = z.infer<typeof DiscordEmojiOptionSchema>;

/**
 * Known slash command names, aggregated from every module's
 * `command-definitions.ts` (`SlashCommandBuilder#setName`). Used by the
 * dashboard's command-management page to list overridable commands without
 * requiring a live bot connection.
 */
export const KNOWN_COMMAND_NAMES = [
  'ban', 'unban', 'kick', 'timeout', 'untimeout', 'warn', 'purge', 'slowmode', 'lock', 'unlock',
  'nick', 'case', 'modnote', 'automod', 'welcome', 'verify', 'rank', 'leaderboard', 'xp', 'balance',
  'daily', 'weekly', 'work', 'pay', 'gamble', 'slots', 'blackjack', 'coinflip', 'shop', 'inventory',
  'eco', 'userinfo', 'serverinfo', 'roleinfo', 'channelinfo', 'avatar', 'banner', 'poll', 'remindme',
  'reminders', 'afk', 'emoji', 'sticker', 'timestamp', 'snipe', 'editsnipe', 'embed', '8ball', 'dice',
  'flip', 'rps', 'choose', 'trivia', 'tictactoe', 'connect4', 'hangman', 'meme', 'cat', 'dog',
  'giveaway', 'ticket', 'selfroles', 'tag', 'starboard', 'suggest', 'suggestion', 'birthday', 'voice',
  'stats', 'invites', 'feeds', 'schedule', 'announce', 'backup', 'help', 'info', 'invite', 'support',
  'privacy', 'gdpr'
] as const;
export type KnownCommandName = (typeof KNOWN_COMMAND_NAMES)[number];
