import { EmbedBuilder, type Message, type PartialMessage } from 'discord.js';
import { t, tf } from '@nexumi/shared';
import type { BotContext } from '../../types.js';

const SNIPE_TTL_SECONDS = 3600;
const DELETE_KEY_PREFIX = 'util:snipe:delete:';
const EDIT_KEY_PREFIX = 'util:snipe:edit:';

type SnipePayload = {
  authorId: string;
  authorTag: string;
  content: string;
  createdAt: number;
  attachmentUrl?: string;
};

function snipeKey(prefix: string, channelId: string): string {
  return `${prefix}${channelId}`;
}

async function storeSnipe(
  redis: BotContext['redis'],
  prefix: string,
  channelId: string,
  payload: SnipePayload
): Promise<void> {
  await redis.set(snipeKey(prefix, channelId), JSON.stringify(payload), 'EX', SNIPE_TTL_SECONDS);
}

async function getSnipe(
  redis: BotContext['redis'],
  prefix: string,
  channelId: string
): Promise<SnipePayload | null> {
  const raw = await redis.get(snipeKey(prefix, channelId));
  if (!raw) {
    return null;
  }
  try {
    return JSON.parse(raw) as SnipePayload;
  } catch {
    return null;
  }
}

function messageToPayload(message: Message | PartialMessage): SnipePayload {
  return {
    authorId: message.author?.id ?? 'unknown',
    authorTag: message.author?.tag ?? 'Unknown',
    content: message.content?.slice(0, 2000) ?? '',
    createdAt: message.createdTimestamp ?? Date.now(),
    attachmentUrl: message.attachments.first()?.url
  };
}

export function registerSnipeEvents(context: BotContext): void {
  context.client.on('messageDelete', async (message) => {
    if (message.author?.bot || !message.guildId || !message.channelId) {
      return;
    }
    if (!(await isSnipeEnabled(context, message.guildId))) {
      return;
    }
    if (!message.content && message.attachments.size === 0) {
      return;
    }
    await storeSnipe(context.redis, DELETE_KEY_PREFIX, message.channelId, messageToPayload(message));
  });

  context.client.on('messageUpdate', async (oldMessage, newMessage) => {
    if (oldMessage.author?.bot || !oldMessage.guildId || !oldMessage.channelId) {
      return;
    }
    if (!(await isSnipeEnabled(context, oldMessage.guildId))) {
      return;
    }
    const oldContent = oldMessage.content ?? '';
    const newContent = newMessage.content ?? '';
    if (!oldContent || oldContent === newContent) {
      return;
    }
    await storeSnipe(context.redis, EDIT_KEY_PREFIX, oldMessage.channelId, messageToPayload(oldMessage));
  });
}

export async function isSnipeEnabled(context: BotContext, guildId: string): Promise<boolean> {
  const settings = await context.prisma.guildSettings.findUnique({
    where: { guildId },
    select: { snipeEnabled: true }
  });
  return settings?.snipeEnabled ?? false;
}

export async function getDeletedSnipe(
  context: BotContext,
  channelId: string,
  locale: 'de' | 'en'
): Promise<{ embed: EmbedBuilder; privacyNote: string } | null> {
  const payload = await getSnipe(context.redis, DELETE_KEY_PREFIX, channelId);
  if (!payload) {
    return null;
  }
  return buildSnipeReply(payload, locale, 'delete');
}

export async function getEditedSnipe(
  context: BotContext,
  channelId: string,
  locale: 'de' | 'en'
): Promise<{ embed: EmbedBuilder; privacyNote: string } | null> {
  const payload = await getSnipe(context.redis, EDIT_KEY_PREFIX, channelId);
  if (!payload) {
    return null;
  }
  return buildSnipeReply(payload, locale, 'edit');
}

function buildSnipeReply(
  payload: SnipePayload,
  locale: 'de' | 'en',
  type: 'delete' | 'edit'
): { embed: EmbedBuilder; privacyNote: string } {
  const embed = new EmbedBuilder()
    .setColor(0x6366f1)
    .setAuthor({ name: payload.authorTag })
    .setDescription(payload.content || '—')
    .setTimestamp(payload.createdAt);

  if (payload.attachmentUrl) {
    embed.setImage(payload.attachmentUrl);
  }

  embed.setFooter({
    text: tf(locale, type === 'delete' ? 'utility.snipe.deletedAt' : 'utility.snipe.editedAt', {
      user: `<@${payload.authorId}>`
    })
  });

  return {
    embed,
    privacyNote: t(locale, 'utility.snipe.privacy')
  };
}
