import {
  SlashCommandBuilder,
  ChatInputCommandInteraction,
  PermissionFlagsBits,
  ChannelType,
  EmbedBuilder,
  Colors,
  TextChannel,
} from 'discord.js';
import { prisma } from '../database';
import { SupportedLocale, t } from '../i18n';
import { EventService } from '../services/EventService';

const SEOUL_TIMEZONE = 'Asia/Seoul';
const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
const TIME_RE = /^\d{2}:\d{2}$/;

function parseSeoulDateTime(date: string, time: string): Date | null {
  if (!DATE_RE.test(date) || !TIME_RE.test(time)) return null;

  const [year, month, day] = date.split('-').map(Number);
  const [hour, minute] = time.split(':').map(Number);

  if (
    Number.isNaN(year) || Number.isNaN(month) || Number.isNaN(day) ||
    Number.isNaN(hour) || Number.isNaN(minute) ||
    month < 1 || month > 12 ||
    day < 1 || day > 31 ||
    hour < 0 || hour > 23 ||
    minute < 0 || minute > 59
  ) {
    return null;
  }

  const utcMillis = Date.UTC(year, month - 1, day, hour - 9, minute, 0, 0);
  const parsed = new Date(utcMillis);

  if (
    parsed.getUTCFullYear() !== year ||
    parsed.getUTCMonth() !== month - 1 ||
    parsed.getUTCDate() !== day ||
    parsed.getUTCHours() !== hour - 9 ||
    parsed.getUTCMinutes() !== minute
  ) {
    return null;
  }

  return parsed;
}

function toDiscordTimestamps(date: Date): { full: string; relative: string } {
  const unix = Math.floor(date.getTime() / 1000);
  return {
    full: `<t:${unix}:F>`,
    relative: `<t:${unix}:R>`,
  };
}

function parseReminderOffsets(raw: string | null): number[] | null {
  if (!raw || raw.trim() === '') return [];

  const values = raw
    .split(',')
    .map(part => part.trim())
    .filter(Boolean);

  if (values.length === 0) return [];

  const offsets = values.map(value => Number(value));
  if (offsets.some(offset => !Number.isInteger(offset) || offset < 0)) {
    return null;
  }

  return Array.from(new Set(offsets)).sort((a, b) => b - a);
}

function formatReminderOffsets(offsets: number[], locale: SupportedLocale): string {
  if (offsets.length === 0) {
    return t(locale, 'commands.event.reminderNone');
  }

  return offsets.map(offset => `${offset}m`).join(', ');
}

function buildStatusLabel(status: 'SCHEDULED' | 'CANCELLED' | 'COMPLETED', locale: SupportedLocale): string {
  if (status === 'SCHEDULED') {
    return t(locale, 'commands.event.statusScheduled');
  }

  if (status === 'COMPLETED') {
    return t(locale, 'commands.event.statusCompleted');
  }

  return t(locale, 'commands.event.statusCancelled');
}

export default {
  data: new SlashCommandBuilder()
    .setName('event')
    .setDescription('Manage scheduled server events.')
    .setDescriptionLocalizations({
      ko: '서버 이벤트 일정을 관리합니다.',
    })
    .setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild)
    .addSubcommand(subcommand =>
      subcommand
        .setName('create')
        .setDescription('Create a new server event.')
        .setDescriptionLocalizations({ ko: '새 서버 이벤트를 생성합니다.' })
        .addStringOption(option =>
          option
            .setName('title')
            .setDescription('Event title')
            .setDescriptionLocalizations({ ko: '이벤트 제목' })
            .setRequired(true)
            .setMaxLength(100)
        )
        .addStringOption(option =>
          option
            .setName('date')
            .setDescription('Date in YYYY-MM-DD format')
            .setDescriptionLocalizations({ ko: 'YYYY-MM-DD 형식의 날짜' })
            .setRequired(true)
        )
        .addStringOption(option =>
          option
            .setName('time')
            .setDescription('Time in HH:mm format (24-hour, Asia/Seoul)')
            .setDescriptionLocalizations({ ko: 'HH:mm 형식의 시간 (24시간제, Asia/Seoul 기준)' })
            .setRequired(true)
        )
        .addStringOption(option =>
          option
            .setName('description')
            .setDescription('Optional event description')
            .setDescriptionLocalizations({ ko: '선택 사항인 이벤트 설명' })
            .setRequired(false)
            .setMaxLength(1000)
        )
        .addChannelOption(option =>
          option
            .setName('channel')
            .setDescription('Optional announcement channel')
            .setDescriptionLocalizations({ ko: '선택 사항인 공지 채널' })
            .addChannelTypes(ChannelType.GuildText)
            .setRequired(false)
        )
        .addStringOption(option =>
          option
            .setName('reminders')
            .setDescription('Reminder offsets in minutes, for example 0,10,60')
            .setDescriptionLocalizations({ ko: '분 단위 리마인더 목록, 예: 0,10,60' })
            .setRequired(false)
        )
    )
    .addSubcommand(subcommand =>
      subcommand
        .setName('list')
        .setDescription('List upcoming server events.')
        .setDescriptionLocalizations({ ko: '예정된 서버 이벤트 목록을 조회합니다.' })
    )
    .addSubcommand(subcommand =>
      subcommand
        .setName('cancel')
        .setDescription('Cancel a scheduled server event.')
        .setDescriptionLocalizations({ ko: '예약된 서버 이벤트를 취소합니다.' })
        .addStringOption(option =>
          option
            .setName('id')
            .setDescription('Event ID to cancel')
            .setDescriptionLocalizations({ ko: '취소할 이벤트 ID' })
            .setRequired(true)
        )
    )
    .addSubcommand(subcommand =>
      subcommand
        .setName('announce')
        .setDescription('Post the event announcement embed again.')
        .setDescriptionLocalizations({ ko: '이벤트 공지 Embed를 다시 게시합니다.' })
        .addStringOption(option =>
          option
            .setName('id')
            .setDescription('Event ID to announce')
            .setDescriptionLocalizations({ ko: '공지할 이벤트 ID' })
            .setRequired(true)
        )
    ),

  async execute(interaction: ChatInputCommandInteraction, locale: SupportedLocale) {
    if (!interaction.guildId) return;

    const subcommand = interaction.options.getSubcommand();

    if (subcommand === 'create') {
      const title = interaction.options.getString('title', true);
      const date = interaction.options.getString('date', true);
      const time = interaction.options.getString('time', true);
      const description = interaction.options.getString('description');
      const channel = interaction.options.getChannel('channel') as TextChannel | null;
      const reminderRaw = interaction.options.getString('reminders');

      const startsAt = parseSeoulDateTime(date, time);
      if (!startsAt) {
        await interaction.editReply({
          content: `${t(locale, 'commands.event.invalidDateTime')}\n${t(locale, 'commands.event.invalidDateTimeResolution')}`,
        });
        return;
      }

      if (startsAt.getTime() <= Date.now()) {
        await interaction.editReply({
          content: `${t(locale, 'commands.event.invalidPastDateTime')}\n${t(locale, 'commands.event.invalidPastDateTimeResolution')}`,
        });
        return;
      }

      const reminderOffsets = parseReminderOffsets(reminderRaw);
      if (!reminderOffsets) {
        await interaction.editReply({
          content: `${t(locale, 'commands.event.invalidReminderOffsets')}\n${t(locale, 'commands.event.invalidReminderOffsetsResolution')}`,
        });
        return;
      }

      const event = await prisma.guildEvent.create({
        data: {
          guildId: interaction.guildId,
          title,
          description,
          startsAt,
          timezone: SEOUL_TIMEZONE,
          announcementChannelId: channel?.id ?? null,
          createdByUserId: interaction.user.id,
          reminderEnabled: reminderOffsets.length > 0,
          reminderOffsets,
        },
      });

      const timestamps = toDiscordTimestamps(event.startsAt);
      const embed = new EmbedBuilder()
        .setColor(Colors.Blurple)
        .setTitle(t(locale, 'commands.event.createSuccessTitle'))
        .setDescription(t(locale, 'commands.event.createSuccessBody', { title: event.title }))
        .addFields(
          { name: t(locale, 'commands.event.fields.eventId'), value: `\`${event.id}\``, inline: false },
          { name: t(locale, 'commands.event.fields.startsAt'), value: timestamps.full, inline: true },
          { name: t(locale, 'commands.event.fields.reminder'), value: formatReminderOffsets(reminderOffsets, locale), inline: true },
          { name: t(locale, 'commands.event.fields.announcementChannel'), value: channel ? `${channel}` : t(locale, 'commands.event.announcementChannelNone'), inline: true },
        )
        .setTimestamp();

      await interaction.editReply({ embeds: [embed] });
      return;
    }

    if (subcommand === 'list') {
      const events = await prisma.guildEvent.findMany({
        where: {
          guildId: interaction.guildId,
          status: 'SCHEDULED',
          startsAt: { gt: new Date() },
        },
        orderBy: { startsAt: 'asc' },
        take: 10,
      });

      if (events.length === 0) {
        await interaction.editReply({
          content: t(locale, 'commands.event.listEmpty'),
        });
        return;
      }

      const embed = new EmbedBuilder()
        .setColor(Colors.Green)
        .setTitle(t(locale, 'commands.event.listTitle'))
        .setTimestamp();

      for (const event of events) {
        const timestamps = toDiscordTimestamps(event.startsAt);
        embed.addFields({
          name: `${event.title} · \`${event.id}\``,
          value: t(locale, 'commands.event.listItemValue', {
            startsAt: timestamps.full,
            relative: timestamps.relative,
            status: buildStatusLabel(event.status, locale),
            reminder: formatReminderOffsets(event.reminderOffsets, locale),
            channel: event.announcementChannelId ? `<#${event.announcementChannelId}>` : t(locale, 'commands.event.announcementChannelNone'),
          }),
          inline: false,
        });
      }

      await interaction.editReply({ embeds: [embed] });
      return;
    }

    if (subcommand === 'cancel') {
      const id = interaction.options.getString('id', true);

      const event = await prisma.guildEvent.findFirst({
        where: {
          id,
          guildId: interaction.guildId,
          status: 'SCHEDULED',
        },
      });

      if (!event) {
        await interaction.editReply({
          content: t(locale, 'commands.event.cancelNotFound', { id }),
        });
        return;
      }

      await prisma.guildEvent.update({
        where: { id: event.id },
        data: { status: 'CANCELLED' },
      });

      await interaction.editReply({
        content: t(locale, 'commands.event.cancelSuccess', { id }),
      });
      return;
    }

    if (subcommand === 'announce') {
      const id = interaction.options.getString('id', true);
      const event = await prisma.guildEvent.findFirst({
        where: {
          id,
          guildId: interaction.guildId,
        },
      });

      if (!event) {
        await interaction.editReply({
          content: t(locale, 'commands.event.cancelNotFound', { id }),
        });
        return;
      }

      if (!event.announcementChannelId) {
        await interaction.editReply({
          content: t(locale, 'commands.event.announceNotAvailable'),
        });
        return;
      }

      try {
        await EventService.announceEvent(interaction.guild!, event.id);
        await interaction.editReply({
          content: t(locale, 'commands.event.announceSuccess', {
            id,
            channel: `<#${event.announcementChannelId}>`,
          }),
        });
      } catch {
        await interaction.editReply({
          content: t(locale, 'commands.event.announceNotAvailable'),
        });
      }
    }
  },
};
