import { Client, EmbedBuilder, TextChannel, ActionRowBuilder, ButtonBuilder, ButtonStyle, ButtonInteraction, Guild } from 'discord.js';
import { prisma } from '../database';
import { settingsStore } from '../config/state';
import { logger } from '../utils/logger';

export type RepeatType = 'none' | 'daily' | 'weekly' | 'monthly';

export class EventService {
  private client: Client | null = null;
  private timer: NodeJS.Timeout | null = null;

  public setClient(client: Client) {
    this.client = client;
  }

  public startScheduler(intervalMs = 60000) {
    if (this.timer) clearInterval(this.timer);
    const interval = Math.max(30000, intervalMs);
    this.timer = setInterval(() => this.tick().catch((err) => logger.warn('event scheduler failed', err)), interval);
  }

  public stopScheduler() {
    if (this.timer) clearInterval(this.timer);
    this.timer = null;
  }

  public async tick() {
    const now = new Date();
    const soon = new Date(now.getTime() + 30 * 60 * 1000);
    const events = await prisma.event.findMany({
      where: { isActive: true, startTime: { lte: soon } },
      orderBy: { startTime: 'asc' }
    });
    for (const ev of events) {
      await this.processEvent(ev, now);
    }
  }

  private async processEvent(ev: any, now: Date) {
    const reminderAt = new Date(ev.startTime.getTime() - (ev.reminderOffsetMinutes ?? 60) * 60000);
    if (ev.lastReminderAt) {
      // already reminded for this start time
    } else if (now >= reminderAt) {
      await this.sendReminder(ev);
      await prisma.event.update({ where: { id: ev.id }, data: { lastReminderAt: new Date() } });
    }
    if (now >= ev.startTime) {
      if (ev.repeatType && ev.repeatType !== 'none') {
        const nextTime = this.computeNextStart(ev.startTime, ev.repeatType, ev.repeatConfig);
        if (nextTime) {
          await prisma.event.update({ where: { id: ev.id }, data: { startTime: nextTime, lastReminderAt: null } });
        } else {
          await prisma.event.update({ where: { id: ev.id }, data: { isActive: false } });
        }
      } else {
        await prisma.event.update({ where: { id: ev.id }, data: { isActive: false } });
      }
    }
  }

  private computeNextStart(current: Date, repeat: RepeatType, cfg: any) {
    const base = new Date(current.getTime());
    if (repeat === 'daily') {
      base.setDate(base.getDate() + 1);
      return base;
    }
    if (repeat === 'weekly') {
      const days = Array.isArray(cfg?.days) ? cfg.days.map((d: any) => Number(d)).filter((n: number) => !isNaN(n)) : [];
      if (!days.length) {
        base.setDate(base.getDate() + 7);
        return base;
      }
      const currentDay = current.getDay();
      const sorted = days.sort((a: number, b: number) => a - b);
      const next = sorted.find((d: number) => d > currentDay);
      const targetDay = next ?? sorted[0];
      const diff = targetDay > currentDay ? targetDay - currentDay : 7 - (currentDay - targetDay);
      base.setDate(base.getDate() + diff);
      return base;
    }
    if (repeat === 'monthly') {
      const day = Number(cfg?.day || current.getDate());
      const nextMonth = current.getMonth() + 1;
      const year = current.getFullYear() + Math.floor(nextMonth / 12);
      const month = nextMonth % 12;
      const res = new Date(current.getTime());
      res.setFullYear(year);
      res.setMonth(month);
      res.setDate(Math.min(day || 1, 28));
      return res;
    }
    return null;
  }

  private async sendReminder(ev: any) {
    if (!this.client) return;
    const guild = this.client.guilds.cache.get(ev.guildId) ?? (await this.client.guilds.fetch(ev.guildId).catch(() => null));
    if (!guild) return;
    const settings = settingsStore.get(ev.guildId) || {};
    if ((settings as any).eventsEnabled === false) return;
    const channel = await guild.channels.fetch(ev.channelId).catch(() => null);
    if (!channel || !channel.isTextBased()) return;
    const signups = await prisma.eventSignup.count({ where: { eventId: ev.id, canceledAt: null } });
    const embed = new EmbedBuilder()
      .setTitle(ev.title || 'Event')
      .setDescription(ev.description || 'Event Erinnerung')
      .addFields(
        { name: 'Start', value: `<t:${Math.floor(ev.startTime.getTime() / 1000)}:f>`, inline: true },
        { name: 'Wiederholung', value: ev.repeatType || 'none', inline: true },
        { name: 'Anmeldungen', value: String(signups || 0), inline: true }
      )
      .setColor(0xf97316);
    const row = new ActionRowBuilder<ButtonBuilder>().addComponents(
      new ButtonBuilder().setCustomId(`event:signup:${ev.id}`).setStyle(ButtonStyle.Success).setLabel('Anmelden'),
      new ButtonBuilder().setCustomId(`event:signoff:${ev.id}`).setStyle(ButtonStyle.Secondary).setLabel('Abmelden')
    );
    const content = ev.roleId ? `<@&${ev.roleId}>` : undefined;
    await (channel as TextChannel).send({ content, embeds: [embed], components: [row] }).catch(() => undefined);
  }

  public async handleButton(interaction: ButtonInteraction, action: 'signup' | 'signoff', eventId: string) {
    if (!interaction.guildId) return;
    const ev = await prisma.event.findFirst({ where: { id: eventId, guildId: interaction.guildId } });
    if (!ev) {
      await interaction.reply({ content: 'Event nicht gefunden.', ephemeral: true });
      return;
    }
    if (!ev.isActive) {
      await interaction.reply({ content: 'Dieses Event ist inaktiv.', ephemeral: true });
      return;
    }
    if (action === 'signup') {
      await prisma.eventSignup.upsert({
        where: { eventId_userId: { eventId: ev.id, userId: interaction.user.id } },
        update: { canceledAt: null },
        create: { eventId: ev.id, guildId: interaction.guildId, userId: interaction.user.id }
      });
      await interaction.reply({ content: 'Du bist angemeldet.', ephemeral: true });
    } else {
      const existing = await prisma.eventSignup.findFirst({ where: { eventId: ev.id, userId: interaction.user.id, canceledAt: null } });
      if (!existing) {
        await interaction.reply({ content: 'Du warst nicht angemeldet.', ephemeral: true });
        return;
      }
      await prisma.eventSignup.update({ where: { id: existing.id }, data: { canceledAt: new Date() } });
      await interaction.reply({ content: 'Abmeldung gespeichert.', ephemeral: true });
    }
  }

  public async listEvents(guildId: string) {
    return prisma.event.findMany({ where: { guildId }, orderBy: { startTime: 'asc' }, include: { _count: { select: { signups: { where: { canceledAt: null } } as any } } as any } as any } as any);
  }

  public async saveEvent(data: any) {
    const base = {
      guildId: data.guildId,
      title: data.title,
      description: data.description,
      channelId: data.channelId,
      startTime: new Date(data.startTime),
      repeatType: data.repeatType || 'none',
      repeatConfig: data.repeatConfig || {},
      reminderOffsetMinutes: data.reminderOffsetMinutes ?? 60,
      roleId: data.roleId || null,
      isActive: data.isActive !== false
    };
    if (data.id) {
      return prisma.event.update({ where: { id: data.id }, data: base });
    }
    return prisma.event.create({ data: base });
  }

  public async deleteEvent(guildId: string, id: string) {
    await prisma.eventSignup.deleteMany({ where: { eventId: id, guildId } }).catch(() => undefined);
    return prisma.event.delete({ where: { id } }).catch(() => undefined);
  }
}
