import type { ChatInputCommandInteraction, GuildMember } from 'discord.js';
import type { BotContext } from './types.js';
import { redis } from './redis.js';
import {
  isChannelAllowed,
  isRoleAllowed,
  type CommandGateReason
} from './command-gate-rules.js';

export type { CommandGateReason };
export type CommandGateResult =
  | { ok: true }
  | { ok: false; reason: CommandGateReason; retryAfterSeconds?: number };

function memberRoleIds(member: GuildMember | null): string[] {
  if (!member) {
    return [];
  }
  return [...member.roles.cache.keys()];
}

/**
 * Applies guild-wide command channel rules and per-command overrides.
 * Empty allow-lists mean "no restriction"; deny-lists always block.
 * Per-command allow/deny is evaluated after global channel rules.
 */
export async function evaluateCommandGate(
  interaction: ChatInputCommandInteraction,
  context: BotContext
): Promise<CommandGateResult> {
  if (!interaction.guildId) {
    return { ok: true };
  }

  const [settings, override] = await Promise.all([
    context.prisma.guildSettings.findUnique({ where: { guildId: interaction.guildId } }),
    context.prisma.commandOverride.findUnique({
      where: {
        guildId_commandName: {
          guildId: interaction.guildId,
          commandName: interaction.commandName
        }
      }
    })
  ]);

  if (override && !override.enabled) {
    return { ok: false, reason: 'disabled' };
  }

  const channelId = interaction.channelId;
  const globalChannelBlock = isChannelAllowed(
    channelId,
    settings?.commandAllowedChannelIds ?? [],
    settings?.commandDeniedChannelIds ?? []
  );
  if (globalChannelBlock) {
    return { ok: false, reason: globalChannelBlock };
  }

  if (override) {
    const perCommandChannelBlock = isChannelAllowed(
      channelId,
      override.allowedChannelIds,
      override.deniedChannelIds
    );
    if (perCommandChannelBlock) {
      return { ok: false, reason: perCommandChannelBlock };
    }

    const member =
      interaction.member && 'roles' in interaction.member
        ? (interaction.member as GuildMember)
        : null;
    const roleIds = memberRoleIds(member);
    const roleBlock = isRoleAllowed(roleIds, override.allowedRoleIds, override.deniedRoleIds);
    if (roleBlock) {
      return { ok: false, reason: roleBlock };
    }

    if (override.cooldownSeconds && override.cooldownSeconds > 0) {
      const key = `command:cooldown:${interaction.guildId}:${interaction.commandName}:${interaction.user.id}`;
      const existing = await redis.ttl(key);
      if (existing > 0) {
        return { ok: false, reason: 'cooldown', retryAfterSeconds: existing };
      }
    }
  }

  return { ok: true };
}

/** Sets the per-command cooldown after a successful execute. */
export async function applyCommandCooldown(
  interaction: ChatInputCommandInteraction,
  context: BotContext
): Promise<void> {
  if (!interaction.guildId) {
    return;
  }
  const override = await context.prisma.commandOverride.findUnique({
    where: {
      guildId_commandName: {
        guildId: interaction.guildId,
        commandName: interaction.commandName
      }
    }
  });
  if (!override?.cooldownSeconds || override.cooldownSeconds <= 0) {
    return;
  }
  const key = `command:cooldown:${interaction.guildId}:${interaction.commandName}:${interaction.user.id}`;
  await redis.set(key, '1', 'EX', override.cooldownSeconds);
}
