import { NextFunction, Request, Response, Router } from 'express';
import { prisma } from '../../database/index';
import { settingsStore } from '../../config/state';
import { context } from '../../config/context';
import { LoggingService } from '../../services/loggingService';
import { env } from '../../config/env';

const router = Router();
const moduleService = context.modules;

function requireAuth(req: Request, res: Response, next: NextFunction) {
  if (!req.session?.user) {
    return res.status(401).json({ error: 'unauthorized', login: '/auth/discord' });
  }
  next();
}

function requireAdmin(req: Request, res: Response, next: NextFunction) {
  const userId = req.session?.user?.id;
  const allowed = Array.isArray(env.ownerIds) ? env.ownerIds.filter(Boolean) : [];
  if (!userId || !allowed.includes(userId)) {
    return res.status(403).json({ error: 'forbidden' });
  }
  next();
}

router.get('/me', requireAuth, (req, res) => {
  const allowed = Array.isArray(env.ownerIds) ? env.ownerIds.filter(Boolean) : [];
  const isAdmin = !!req.session.user && allowed.includes(req.session.user.id);
  res.json({ user: { ...req.session.user, isAdmin } });
});

router.get('/guilds', requireAuth, (req, res) => {
  const sessionGuilds = Array.isArray(req.session?.guilds) ? req.session.guilds : [];
  // Only allow guilds the user owns or can manage (manage_guild or admin) and where the bot is present
  const allowedIds = new Set(
    sessionGuilds
      .filter((g: any) => {
        if (!g) return false;
        if (g.owner) return true;
        const perms = g.permissions ? BigInt(g.permissions) : 0n;
        const hasAdmin = (perms & 0x8n) === 0x8n;
        const hasManageGuild = (perms & 0x20n) === 0x20n;
        return hasAdmin || hasManageGuild;
      })
      .map((g: any) => g.id)
  );
  const guilds =
    context.client?.guilds.cache
      .filter((g) => allowedIds.has(g.id))
      .map((g) => ({
        id: g.id,
        name: g.name,
        icon: g.icon
      })) ?? [];
  res.json({ guilds });
});

router.get('/guild/info', requireAuth, async (req, res) => {
  const guildId = typeof req.query.guildId === 'string' ? req.query.guildId : undefined;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  const guild =
    context.client?.guilds.cache.get(guildId) || (await context.client?.guilds.fetch(guildId).catch(() => null));
  if (!guild) return res.status(404).json({ error: 'guild not found' });
  const owner = await guild.fetchOwner().catch(() => null);
  const channels = guild.channels.cache;
  const textCount = channels.filter((c) => c.isTextBased()).size;
  const voiceCount = channels.filter((c) => c.isVoiceBased()).size;
  const modules = settingsStore.get(guildId) || {};
  res.json({
    guild: {
      id: guild.id,
      name: guild.name,
      icon: guild.icon,
      memberCount: guild.memberCount,
      createdAt: guild.createdAt?.getTime?.() || null,
      owner: owner ? { id: owner.id, tag: owner.user?.tag } : null,
      textCount,
      voiceCount,
      modules: {
        ticketsEnabled: modules.ticketsEnabled !== false,
        automodEnabled: modules.automodEnabled !== false,
        musicEnabled: modules.musicEnabled !== false,
        welcomeEnabled: modules.welcomeConfig?.enabled !== false,
        dynamicVoiceEnabled: modules.dynamicVoiceEnabled !== false,
        statuspageEnabled: (modules as any).statuspageEnabled !== false,
        birthdayEnabled: (modules as any).birthdayEnabled !== false,
        reactionRolesEnabled: (modules as any).reactionRolesEnabled !== false,
        serverStatsEnabled: (modules as any).serverStatsEnabled === true || (modules as any).serverStatsConfig?.enabled === true
      }
    }
  });
});

router.get('/guild/activity', requireAuth, (req, res) => {
  const guildId = typeof req.query.guildId === 'string' ? req.query.guildId : undefined;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  const activity = context.admin.getGuildActivity(guildId);
  res.json({ activity });
});

router.get('/guild/logs', requireAuth, (req, res) => {
  const guildId = typeof req.query.guildId === 'string' ? req.query.guildId : undefined;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  const logs = context.admin.getGuildLogs(guildId).slice(0, 100);
  res.json({ logs });
});

router.get('/guild/resources', requireAuth, async (req, res) => {
  const guildId = typeof req.query.guildId === 'string' ? req.query.guildId : undefined;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  const guild = context.client?.guilds.cache.get(guildId);
  if (!guild) return res.status(404).json({ error: 'guild not found' });
  const channels = guild.channels.cache
    .filter((c) => c.isTextBased() || c.isVoiceBased())
    .map((c) => ({
      id: c.id,
      name: c.name,
      type: c.isVoiceBased() ? 'voice' : 'text',
      parentId: c.parentId
    }))
    .sort((a, b) => a.name.localeCompare(b.name));
  const roles = guild.roles.cache
    .filter((r) => r.name !== '@everyone')
    .map((r) => ({
      id: r.id,
      name: r.name,
      color: r.hexColor
    }))
    .sort((a, b) => b.rawPosition - a.rawPosition);
  const categories = guild.channels.cache
    .filter((c) => c.type === 4)
    .map((c) => ({ id: c.id, name: c.name }))
    .sort((a, b) => a.name.localeCompare(b.name));
  res.json({ channels, roles, categories });
});

router.get('/overview', requireAuth, async (req, res) => {
  const guildId = typeof req.query.guildId === 'string' ? req.query.guildId : undefined;
  try {
    const [open, inProgress, closed] = await Promise.all([
      prisma.ticket.count({ where: { status: 'open', ...(guildId ? { guildId } : {}) } }),
      prisma.ticket.count({ where: { status: 'in-progress', ...(guildId ? { guildId } : {}) } }),
      prisma.ticket.count({ where: { status: 'closed', ...(guildId ? { guildId } : {}) } })
    ]);
    // TODO: MODULE: Musik-Status mit Modul-Flag und pro Guild Sessions anreichern (aktiv/inaktiv, aktueller Track).
    const music = context.music.getStatus();
    res.json({ tickets: { open, inProgress, closed }, music });
  } catch (err) {
    res.json({ tickets: { open: 0, inProgress: 0, closed: 0 }, error: 'DB unavailable' });
  }
});

router.get('/admin/overview', requireAuth, requireAdmin, (_req, res) => {
  const overview = context.admin.getOverview();
  res.json({ overview });
});

router.get('/admin/activity', requireAuth, requireAdmin, (_req, res) => {
  const activity = context.admin.getActivity();
  res.json(activity);
});

router.get('/admin/logs', requireAuth, requireAdmin, (_req, res) => {
  res.json({ logs: context.admin.getLogs() });
});

router.get('/tickets', requireAuth, async (req, res) => {
  const guildId = typeof req.query.guildId === 'string' ? req.query.guildId : undefined;
  const status = typeof req.query.status === 'string' ? req.query.status : undefined;
  const take = Math.min(Number(req.query.take ?? 20) || 20, 100);
  const where: Record<string, any> = {};
  if (guildId) where.guildId = guildId;
  if (status) where.status = status;
  try {
    // TODO: TICKETS: Filter/Suche/Sortierung per Query-Params erweitern (Status, Claim, Priorität, Zeitfenster) für Dashboard.
    const tickets = await prisma.ticket.findMany({ where, orderBy: { createdAt: 'desc' }, take });
    res.json({ tickets });
  } catch (err) {
    res.json({ tickets: [], error: 'DB unavailable' });
  }
});

router.get('/tickets/pipeline', requireAuth, async (req, res) => {
  const guildId = typeof req.query.guildId === 'string' ? req.query.guildId : undefined;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  const tickets = await prisma.ticket.findMany({ where: { guildId }, orderBy: { createdAt: 'desc' }, take: 200 });
  const grouped = { neu: [], in_bearbeitung: [], warten_auf_user: [], erledigt: [] } as Record<string, any[]>;
  tickets.forEach((t) => {
    const statusVal = ['neu', 'in_bearbeitung', 'warten_auf_user', 'erledigt'].includes(t.status) ? t.status : 'neu';
    (grouped as any)[statusVal].push(t);
  });
  res.json({ pipeline: grouped });
});

router.post('/tickets/:id/status', requireAuth, async (req, res) => {
  const ticketId = req.params.id;
  const statusVal = typeof req.body.status === 'string' ? req.body.status : '';
  if (!statusVal) return res.status(400).json({ error: 'status required' });
  const updated = await context.tickets.updateStatus(ticketId, statusVal as any);
  if (!updated) return res.status(404).json({ error: 'not found' });
  res.json({ ticket: updated });
});

router.get('/tickets/sla', requireAuth, async (req, res) => {
  const guildId = typeof req.query.guildId === 'string' ? req.query.guildId : undefined;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  const days = Math.min(Math.max(Number(req.query.days) || 30, 1), 180);
  const since = new Date(Date.now() - days * 24 * 3600 * 1000);
  const tickets = await prisma.ticket.findMany({
    where: { guildId, createdAt: { gte: since } },
    select: { createdAt: true, firstClaimAt: true, firstResponseAt: true, claimedBy: true }
  });
  const supporterStats: Record<
    string,
    { supporterId: string; count: number; ttcSum: number; ttfrSum: number; ttfrCount: number; ttcCount: number }
  > = {};
  const dayStats: Record<
    string,
    { date: string; count: number; ttcSum: number; ttfrSum: number; ttcCount: number; ttfrCount: number }
  > = {};
  tickets.forEach((t) => {
    const dayKey = t.createdAt.toISOString().slice(0, 10);
    if (!dayStats[dayKey]) dayStats[dayKey] = { date: dayKey, count: 0, ttcSum: 0, ttfrSum: 0, ttcCount: 0, ttfrCount: 0 };
    dayStats[dayKey].count += 1;
    if (t.claimedBy && t.firstClaimAt) {
      const diff = t.firstClaimAt.getTime() - t.createdAt.getTime();
      const key = t.claimedBy;
      if (!supporterStats[key])
        supporterStats[key] = { supporterId: key, count: 0, ttcSum: 0, ttfrSum: 0, ttcCount: 0, ttfrCount: 0 };
      supporterStats[key].count += 1;
      supporterStats[key].ttcSum += diff;
      supporterStats[key].ttcCount += 1;
      dayStats[dayKey].ttcSum += diff;
      dayStats[dayKey].ttcCount += 1;
    }
    if (t.firstResponseAt) {
      const diff = t.firstResponseAt.getTime() - t.createdAt.getTime();
      if (t.claimedBy) {
        const key = t.claimedBy;
        if (!supporterStats[key])
          supporterStats[key] = { supporterId: key, count: 0, ttcSum: 0, ttfrSum: 0, ttcCount: 0, ttfrCount: 0 };
        supporterStats[key].ttfrSum += diff;
        supporterStats[key].ttfrCount += 1;
      }
      dayStats[dayKey].ttfrSum += diff;
      dayStats[dayKey].ttfrCount += 1;
    }
  });
  const supporters = Object.values(supporterStats).map((s) => ({
    supporterId: s.supporterId,
    tickets: s.count,
    avgTTC: s.ttcCount ? Math.round(s.ttcSum / s.ttcCount) : null,
    avgTTFR: s.ttfrCount ? Math.round(s.ttfrSum / s.ttfrCount) : null
  }));
  const daysArr = Object.values(dayStats)
    .sort((a, b) => a.date.localeCompare(b.date))
    .map((d) => ({
      date: d.date,
      tickets: d.count,
      avgTTC: d.ttcCount ? Math.round(d.ttcSum / d.ttcCount) : null,
      avgTTFR: d.ttfrCount ? Math.round(d.ttfrSum / d.ttfrCount) : null
    }));
  res.json({ supporters, days: daysArr });
});

router.get('/tickets/:id/transcript', requireAuth, async (req, res) => {
  const id = req.params.id;
  try {
    const ticket = await prisma.ticket.findFirst({ where: { id } });
    if (!ticket || !ticket.transcript) return res.status(404).send('Transcript not found');
    return res.sendFile(ticket.transcript);
  } catch {
    return res.status(500).send('Transcript error');
  }
});

router.get('/tickets/:id/messages', requireAuth, async (req, res) => {
  const id = req.params.id;
  try {
    const ticket = await prisma.ticket.findFirst({ where: { id } });
    if (!ticket) return res.status(404).json({ error: 'not found' });
    const channel = ticket.channelId ? await context.client?.channels.fetch(ticket.channelId) : null;
    if (!channel || !channel.isTextBased()) return res.status(404).json({ error: 'channel missing' });
    // TODO: TICKETS: Live-Messages per WebSocket/Server-Sent-Events streamen statt polling, inkl. Author-Rich-Info.
    const msgs = await (channel as any).messages.fetch({ limit: 50 });
    const data = msgs
      .sort((a, b) => a.createdTimestamp - b.createdTimestamp)
      .map((m: any) => ({
        id: m.id,
        author: { tag: m.author?.tag ?? 'Unknown', avatar: m.author?.displayAvatarURL?.() ?? null },
        content: m.content,
        createdAt: m.createdTimestamp
      }));
    res.json({ messages: data });
  } catch {
    res.status(500).json({ error: 'message fetch failed' });
  }
});

router.post('/tickets/:id/close', requireAuth, async (req, res) => {
  const id = req.params.id;
  try {
    const ticket = await prisma.ticket.findFirst({ where: { id } });
    if (!ticket) return res.status(404).json({ error: 'not found' });
    if (!context.client) return res.status(500).json({ error: 'client unavailable' });
    const channel = await context.client.channels.fetch(ticket.channelId).catch(() => null);
    if (channel && channel.isTextBased()) {
      const transcriptPath = await context.tickets.exportTranscript(channel as any, ticket.id);
      await prisma.ticket.update({ where: { id: ticket.id }, data: { status: 'closed', transcript: transcriptPath } });
      await context.tickets['sendTranscriptToLog'](channel.guild, transcriptPath, ticket as any).catch(() => undefined);
      await (channel as any).delete('Ticket geschlossen');
    } else {
      await prisma.ticket.update({ where: { id: ticket.id }, data: { status: 'closed' } });
    }
    res.json({ ok: true });
  } catch {
    res.status(500).json({ error: 'close failed' });
  }
});

router.post('/tickets/panel', requireAuth, async (req, res) => {
  const { guildId, channelId, title, description, categories } = req.body as {
    guildId?: string;
    channelId?: string;
    title?: string;
    description?: string;
    categories?: { label: string; emoji?: string; customId: string }[];
  };
  if (!guildId || !channelId) return res.status(400).json({ error: 'guildId and channelId required' });
  try {
    // TODO: TICKETS: Panel-Template als Dashboard-Entwurf speichern (DB) statt ad-hoc zu senden; Mehrsprachigkeit berücksichtigen.
    const cfg = settingsStore.get(guildId);
    if (cfg?.ticketsEnabled === false) return res.status(403).json({ error: 'tickets disabled' });
    if (!context.client) return res.status(503).json({ error: 'bot client unavailable' });
    const guild = await context.client.guilds.fetch(guildId);
    const channel = await guild.channels.fetch(channelId);
    if (!channel || !channel.isTextBased()) return res.status(400).json({ error: 'channel not text' });
    const cats = (categories ?? []).filter((c) => c?.label && c?.customId).slice(0, 5);
    const panel =
      cats.length > 0
        ? context.tickets.buildCustomPanel({ title, description, categories: cats })
        : context.tickets.buildPanelEmbed();
    const { embed, buttons } = panel;
    await (channel as any).send({ embeds: [embed], components: [buttons] });
    return res.json({ ok: true });
  } catch (err: any) {
    const code = err?.code || err?.status || err?.name;
    const message = err?.message || 'failed to send panel';
    if (code === 50001 || code === 'Missing Access') return res.status(403).json({ error: 'missing access' });
    if (code === 50013 || /Missing Permissions/i.test(message)) return res.status(403).json({ error: 'missing permissions', detail: message });
    if (code === 10003 || code === 'Unknown Channel') return res.status(404).json({ error: 'channel not found' });
    console.error('tickets/panel error', code, message);
    return res.status(500).json({ error: 'failed to send panel', detail: message });
  }
});

router.get('/tickets/support-login', requireAuth, async (req, res) => {
  const guildId = typeof req.query.guildId === 'string' ? req.query.guildId : undefined;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  const cfg = settingsStore.get(guildId) || {};
  const config = {
    panelChannelId: cfg.supportLoginConfig?.panelChannelId || '',
    panelMessageId: cfg.supportLoginConfig?.panelMessageId || '',
    title: cfg.supportLoginConfig?.title || 'Support Login',
    description: cfg.supportLoginConfig?.description || 'Melde dich als Support an/ab.',
    loginLabel: cfg.supportLoginConfig?.loginLabel || 'Ich bin jetzt im Support',
    logoutLabel: cfg.supportLoginConfig?.logoutLabel || 'Ich bin nicht mehr im Support',
    autoRefresh: cfg.supportLoginConfig?.autoRefresh !== false
  };
  const status = await context.tickets.getSupportStatus(guildId);
  res.json({ config, status, supportRoleId: cfg.supportRoleId || null });
});

router.post('/tickets/support-login', requireAuth, async (req, res) => {
  const guildId = typeof req.body.guildId === 'string' ? req.body.guildId : undefined;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  const config = {
    panelChannelId: typeof req.body.panelChannelId === 'string' ? req.body.panelChannelId : undefined,
    title: typeof req.body.title === 'string' ? req.body.title : undefined,
    description: typeof req.body.description === 'string' ? req.body.description : undefined,
    loginLabel: typeof req.body.loginLabel === 'string' ? req.body.loginLabel : undefined,
    logoutLabel: typeof req.body.logoutLabel === 'string' ? req.body.logoutLabel : undefined,
    autoRefresh: req.body.autoRefresh !== undefined ? !!req.body.autoRefresh : undefined
  };
  await settingsStore.set(guildId, { supportLoginConfig: config } as any);
  const msgId = await context.tickets.publishSupportPanel(guildId, config).catch(() => null);
  if (msgId) await settingsStore.set(guildId, { supportLoginConfig: { ...config, panelMessageId: msgId } } as any);
  res.json({ ok: true, config: { ...config, panelMessageId: msgId || config['panelMessageId'] } });
});

router.get('/events', requireAuth, async (req, res) => {
  const guildId = typeof req.query.guildId === 'string' ? req.query.guildId : undefined;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  const events = await prisma.event.findMany({
    where: { guildId },
    orderBy: { startTime: 'asc' },
    include: { _count: { select: { signups: { where: { canceledAt: null } } as any } as any } as any } as any
  } as any);
  res.json({ events });
});

router.post('/events', requireAuth, async (req, res) => {
  const {
    id,
    guildId,
    title,
    description,
    channelId,
    startTime,
    repeatType,
    repeatConfig,
    reminderOffsetMinutes,
    roleId,
    isActive
  } = req.body || {};
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  try {
    const saved = await context.events.saveEvent({
      id,
      guildId,
      title,
      description,
      channelId,
      startTime,
      repeatType,
      repeatConfig,
      reminderOffsetMinutes,
      roleId,
      isActive
    });
    res.json({ ok: true, event: saved });
  } catch {
    res.status(500).json({ error: 'save failed' });
  }
});

router.delete('/events/:id', requireAuth, async (req, res) => {
  const guildId = typeof req.body.guildId === 'string' ? req.body.guildId : undefined;
  const id = req.params.id;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  await context.events.deleteEvent(guildId, id);
  res.json({ ok: true });
});

router.get('/settings', requireAuth, (req, res) => {
  const guildId = typeof req.query.guildId === 'string' ? req.query.guildId : undefined;
  if (guildId) {
    return res.json({ guildId, settings: settingsStore.get(guildId) ?? {} });
  }
  res.json({ guilds: Array.from(settingsStore.all().entries()) });
});

router.get('/modules', requireAuth, async (req, res) => {
  const guildId = typeof req.query.guildId === 'string' ? req.query.guildId : undefined;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  const modules = await moduleService.getModulesForGuild(guildId);
  res.json({ modules });
});

router.get('/birthday', requireAuth, async (req, res) => {
  const guildId = typeof req.query.guildId === 'string' ? req.query.guildId : undefined;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  const cfg = settingsStore.get(guildId) || {};
  const config = {
    enabled: cfg.birthdayEnabled ?? cfg.birthdayConfig?.enabled ?? true,
    channelId: cfg.birthdayConfig?.channelId ?? '',
    sendHour: cfg.birthdayConfig?.sendHour ?? 9,
    messageTemplate: cfg.birthdayConfig?.messageTemplate ?? 'Alles Gute zum Geburtstag, {user}!'
  };
  const birthdays = await prisma.birthday.findMany({ where: { guildId }, orderBy: { birthDate: 'asc' }, take: 200 });
  res.json({ config, birthdays });
});

router.post('/birthday', requireAuth, async (req, res) => {
  const guildId = typeof req.body.guildId === 'string' ? req.body.guildId : undefined;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  const enabledRaw = req.body.enabled;
  const channelId = typeof req.body.channelId === 'string' ? req.body.channelId.trim() : undefined;
  const template = typeof req.body.messageTemplate === 'string' ? req.body.messageTemplate.slice(0, 500) : undefined;
  const hour = Number(req.body.sendHour);
  const sendHour = Number.isFinite(hour) ? Math.min(23, Math.max(0, Math.round(hour))) : undefined;
  const enabled = typeof enabledRaw === 'string' ? enabledRaw === 'true' : enabledRaw;
  const config = {
    enabled,
    channelId: channelId || undefined,
    sendHour,
    messageTemplate: template
  };
  await settingsStore.set(guildId, { birthdayEnabled: enabled, birthdayConfig: config } as any);
  context.birthdays.invalidate(guildId);
  res.json({ ok: true, config });
});

router.get('/reactionroles', requireAuth, async (req, res) => {
  const guildId = typeof req.query.guildId === 'string' ? req.query.guildId : undefined;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  const sets = await context.reactionRoles.listSets(guildId);
  res.json({ sets });
});

router.post('/reactionroles', requireAuth, async (req, res) => {
  const guildId = typeof req.body.guildId === 'string' ? req.body.guildId : undefined;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  if (!req.body.channelId) return res.status(400).json({ error: 'channelId required' });
  const entries = Array.isArray(req.body.entries)
    ? req.body.entries
        .map((e: any) => ({
          emoji: typeof e.emoji === 'string' ? e.emoji.trim() : '',
          roleId: typeof e.roleId === 'string' ? e.roleId.trim() : '',
          label: typeof e.label === 'string' ? e.label.trim() : undefined,
          description: typeof e.description === 'string' ? e.description.trim() : undefined
        }))
        .filter((e: any) => e.emoji && e.roleId)
    : [];
  if (!entries.length) return res.status(400).json({ error: 'entries required' });
  try {
    const set = await context.reactionRoles.saveSet({
      guildId,
      channelId: typeof req.body.channelId === 'string' ? req.body.channelId : '',
      messageId: typeof req.body.messageId === 'string' ? req.body.messageId : undefined,
      title: typeof req.body.title === 'string' ? req.body.title : undefined,
      description: typeof req.body.description === 'string' ? req.body.description : undefined,
      entries
    });
    res.json({ set });
  } catch (err) {
    res.status(400).json({ error: 'save failed' });
  }
});

router.put('/reactionroles/:id', requireAuth, async (req, res) => {
  const guildId = typeof req.body.guildId === 'string' ? req.body.guildId : undefined;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  const id = req.params.id;
  if (!req.body.channelId) return res.status(400).json({ error: 'channelId required' });
  const entries = Array.isArray(req.body.entries)
    ? req.body.entries
        .map((e: any) => ({
          emoji: typeof e.emoji === 'string' ? e.emoji.trim() : '',
          roleId: typeof e.roleId === 'string' ? e.roleId.trim() : '',
          label: typeof e.label === 'string' ? e.label.trim() : undefined,
          description: typeof e.description === 'string' ? e.description.trim() : undefined
        }))
        .filter((e: any) => e.emoji && e.roleId)
    : [];
  if (!entries.length) return res.status(400).json({ error: 'entries required' });
  try {
    const set = await context.reactionRoles.saveSet({
      id,
      guildId,
      channelId: typeof req.body.channelId === 'string' ? req.body.channelId : '',
      messageId: typeof req.body.messageId === 'string' ? req.body.messageId : undefined,
      title: typeof req.body.title === 'string' ? req.body.title : undefined,
      description: typeof req.body.description === 'string' ? req.body.description : undefined,
      entries
    });
    res.json({ set });
  } catch (err) {
    res.status(400).json({ error: 'save failed' });
  }
});

router.delete('/reactionroles/:id', requireAuth, async (req, res) => {
  const guildId = typeof req.body.guildId === 'string' ? req.body.guildId : undefined;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  const id = req.params.id;
  try {
    await context.reactionRoles.deleteSet(guildId, id);
    res.json({ ok: true });
  } catch {
    res.status(400).json({ error: 'delete failed' });
  }
});

router.get('/register/forms', requireAuth, async (req, res) => {
  const guildId = typeof req.query.guildId === 'string' ? req.query.guildId : undefined;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  const forms = await context.register.listForms(guildId);
  res.json({ forms });
});

router.post('/register/forms', requireAuth, async (req, res) => {
  const guildId = typeof req.body.guildId === 'string' ? req.body.guildId : undefined;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  const data = req.body || {};
  const form = await context.register.saveForm({
    guildId,
    name: data.name || 'Formular',
    description: data.description || '',
    reviewChannelId: data.reviewChannelId || undefined,
    notifyRoleIds: Array.isArray(data.notifyRoleIds) ? data.notifyRoleIds : typeof data.notifyRoleIds === 'string' ? data.notifyRoleIds.split(',').map((s: string) => s.trim()).filter(Boolean) : [],
    isActive: data.isActive !== false,
    fields: Array.isArray(data.fields) ? data.fields : []
  });
  res.json({ form });
});

router.put('/register/forms/:id', requireAuth, async (req, res) => {
  const guildId = typeof req.body.guildId === 'string' ? req.body.guildId : undefined;
  const id = req.params.id;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  const data = req.body || {};
  const form = await context.register.saveForm({
    id,
    guildId,
    name: data.name || 'Formular',
    description: data.description || '',
    reviewChannelId: data.reviewChannelId || undefined,
    notifyRoleIds: Array.isArray(data.notifyRoleIds) ? data.notifyRoleIds : typeof data.notifyRoleIds === 'string' ? data.notifyRoleIds.split(',').map((s: string) => s.trim()).filter(Boolean) : [],
    isActive: data.isActive !== false,
    fields: Array.isArray(data.fields) ? data.fields : []
  });
  res.json({ form });
});

router.delete('/register/forms/:id', requireAuth, async (req, res) => {
  const guildId = typeof req.body.guildId === 'string' ? req.body.guildId : undefined;
  const id = req.params.id;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  const ok = await context.register.deleteForm(guildId, id);
  if (!ok) return res.status(404).json({ error: 'not found' });
  res.json({ ok: true });
});

router.post('/register/forms/:id/panel', requireAuth, async (req, res) => {
  const guildId = typeof req.body.guildId === 'string' ? req.body.guildId : undefined;
  const id = req.params.id;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  const channelId = typeof req.body.channelId === 'string' ? req.body.channelId : undefined;
  const message = typeof req.body.message === 'string' ? req.body.message : undefined;
  const msgId = await context.register.sendPanel(guildId, id, channelId, message);
  if (!msgId) return res.status(400).json({ error: 'panel failed' });
  res.json({ ok: true, messageId: msgId });
});

router.get('/register/apps', requireAuth, async (req, res) => {
  const guildId = typeof req.query.guildId === 'string' ? req.query.guildId : undefined;
  const status = typeof req.query.status === 'string' ? req.query.status : undefined;
  const formId = typeof req.query.formId === 'string' ? req.query.formId : undefined;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  const apps = await context.register.listApplications(guildId, status, formId);
  res.json({ applications: apps });
});

router.get('/register/apps/:id', requireAuth, async (req, res) => {
  const app = await context.register.getApplication(req.params.id);
  if (!app) return res.status(404).json({ error: 'not found' });
  res.json({ application: app });
});

router.get('/automations', requireAuth, async (req, res) => {
  const guildId = typeof req.query.guildId === 'string' ? req.query.guildId : undefined;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  const rules = await context.ticketAutomation.list(guildId);
  res.json({ rules });
});

router.post('/automations', requireAuth, async (req, res) => {
  const guildId = typeof req.body.guildId === 'string' ? req.body.guildId : undefined;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  const rule = await context.ticketAutomation.save({
    guildId,
    name: req.body.name || 'Automation',
    condition: req.body.condition || {},
    action: req.body.action || {},
    active: req.body.active !== false
  });
  res.json({ rule });
});

router.put('/automations/:id', requireAuth, async (req, res) => {
  const guildId = typeof req.body.guildId === 'string' ? req.body.guildId : undefined;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  const id = req.params.id;
  const rule = await context.ticketAutomation.save({
    id,
    guildId,
    name: req.body.name || 'Automation',
    condition: req.body.condition || {},
    action: req.body.action || {},
    active: req.body.active !== false
  });
  res.json({ rule });
});

router.delete('/automations/:id', requireAuth, async (req, res) => {
  const guildId = typeof req.body.guildId === 'string' ? req.body.guildId : undefined;
  const id = req.params.id;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  const ok = await context.ticketAutomation.remove(guildId, id);
  if (!ok) return res.status(404).json({ error: 'not found' });
  res.json({ ok: true });
});

router.get('/kb', requireAuth, async (req, res) => {
  const guildId = typeof req.query.guildId === 'string' ? req.query.guildId : undefined;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  const articles = await context.knowledgeBase.list(guildId);
  res.json({ articles });
});

router.post('/kb', requireAuth, async (req, res) => {
  const guildId = typeof req.body.guildId === 'string' ? req.body.guildId : undefined;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  const keywords =
    typeof req.body.keywords === 'string'
      ? req.body.keywords
          .split(',')
          .map((s: string) => s.trim())
          .filter(Boolean)
      : req.body.keywords || [];
  const article = await context.knowledgeBase.save({
    guildId,
    title: req.body.title || 'Artikel',
    keywords,
    content: req.body.content || ''
  });
  res.json({ article });
});

router.put('/kb/:id', requireAuth, async (req, res) => {
  const guildId = typeof req.body.guildId === 'string' ? req.body.guildId : undefined;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  const id = req.params.id;
  const keywords =
    typeof req.body.keywords === 'string'
      ? req.body.keywords
          .split(',')
          .map((s: string) => s.trim())
          .filter(Boolean)
      : req.body.keywords || [];
  const article = await context.knowledgeBase.save({
    id,
    guildId,
    title: req.body.title || 'Artikel',
    keywords,
    content: req.body.content || ''
  });
  res.json({ article });
});

router.delete('/kb/:id', requireAuth, async (req, res) => {
  const guildId = typeof req.body.guildId === 'string' ? req.body.guildId : undefined;
  const id = req.params.id;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  const ok = await context.knowledgeBase.remove(guildId, id);
  if (!ok) return res.status(404).json({ error: 'not found' });
  res.json({ ok: true });
});

router.get('/statuspage', requireAuth, async (req, res) => {
  const guildId = typeof req.query.guildId === 'string' ? req.query.guildId : undefined;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  const cfg = await context.statuspage.getConfig(guildId);
  res.json({ config: cfg });
});

router.post('/statuspage', requireAuth, async (req, res) => {
  const guildId = typeof req.body.guildId === 'string' ? req.body.guildId : undefined;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  await context.statuspage.saveConfig(guildId, req.body.config || {});
  res.json({ ok: true });
});

router.post('/statuspage/service', requireAuth, async (req, res) => {
  const guildId = typeof req.body.guildId === 'string' ? req.body.guildId : undefined;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  const svc = await context.statuspage.addService(guildId, req.body.service || {});
  res.json({ service: svc });
});

router.put('/statuspage/service/:id', requireAuth, async (req, res) => {
  const guildId = typeof req.body.guildId === 'string' ? req.body.guildId : undefined;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  await context.statuspage.updateService(guildId, req.params.id, req.body.service || {});
  res.json({ ok: true });
});

router.delete('/statuspage/service/:id', requireAuth, async (req, res) => {
  const guildId = typeof req.body.guildId === 'string' ? req.body.guildId : undefined;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  await context.statuspage.deleteService(guildId, req.params.id);
  res.json({ ok: true });
});

router.get('/server-stats', requireAuth, async (req, res) => {
  const guildId = typeof req.query.guildId === 'string' ? req.query.guildId : undefined;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  const cfg = await context.stats.getConfig(guildId);
  res.json({ config: cfg });
});

router.post('/server-stats', requireAuth, async (req, res) => {
  const guildId = typeof req.body.guildId === 'string' ? req.body.guildId : undefined;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  const cfg = await context.stats.saveConfig(guildId, req.body.config || {});
  res.json({ config: cfg });
});

router.post('/server-stats/refresh', requireAuth, async (req, res) => {
  const guildId = typeof req.body.guildId === 'string' ? req.body.guildId : undefined;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  await context.stats.refreshGuild(guildId);
  res.json({ ok: true });
});

router.post('/settings', requireAuth, async (req, res) => {
  const current = req.body.guildId ? settingsStore.get(req.body.guildId) ?? {} : {};
  const {
    guildId,
    welcomeChannelId,
    logChannelId,
    automodEnabled,
    automodConfig,
    loggingConfig,
    welcomeConfig,
    welcomeEnabled,
    levelingEnabled,
    ticketsEnabled,
    musicEnabled,
    dynamicVoiceEnabled,
    dynamicVoiceConfig,
    supportRoleId,
    statuspageEnabled,
    statuspageConfig,
    eventsEnabled,
    birthdayEnabled,
    birthdayConfig,
  reactionRolesEnabled,
  reactionRolesConfig,
  registerEnabled,
  registerConfig,
  serverStatsEnabled,
  serverStatsConfig
} = req.body;
  if (!guildId) return res.status(400).json({ error: 'guildId required' });
  const normalizeArray = (val: any) =>
    Array.isArray(val)
      ? val.map((s) => String(s).trim()).filter(Boolean)
      : typeof val === 'string'
      ? val
          .split(/[,\\n]/)
          .map((s: string) => s.trim())
          .filter(Boolean)
      : [];

  const existingLogging = (current as any).loggingConfig || (current as any).automodConfig?.loggingConfig || {};
  const parsedLogging = {
    logChannelId: loggingConfig?.logChannelId ?? logChannelId ?? existingLogging.logChannelId ?? undefined,
    categories: {
      joinLeave: loggingConfig?.categories?.joinLeave ?? loggingConfig?.joinLeave ?? existingLogging.categories?.joinLeave ?? true,
      messageEdit: loggingConfig?.categories?.messageEdit ?? loggingConfig?.messageEdit ?? existingLogging.categories?.messageEdit ?? true,
      messageDelete: loggingConfig?.categories?.messageDelete ?? loggingConfig?.messageDelete ?? existingLogging.categories?.messageDelete ?? true,
      automodActions: loggingConfig?.categories?.automodActions ?? loggingConfig?.automodActions ?? existingLogging.categories?.automodActions ?? true,
      ticketActions: loggingConfig?.categories?.ticketActions ?? loggingConfig?.ticketActions ?? existingLogging.categories?.ticketActions ?? true,
      musicEvents: loggingConfig?.categories?.musicEvents ?? loggingConfig?.musicEvents ?? existingLogging.categories?.musicEvents ?? true,
      system: loggingConfig?.categories?.system ?? existingLogging.categories?.system ?? true
    }
  };

  let parsedAutomod =
    typeof automodConfig === 'object'
      ? { ...(current as any).automodConfig, ...(automodConfig ?? {}) }
      : automodConfig === undefined
      ? { ...(current as any).automodConfig }
      : {
          ...(current as any).automodConfig,
          spamThreshold: automodConfig?.spamThreshold ? Number(automodConfig.spamThreshold) : undefined,
          windowMs: automodConfig?.windowMs ? Number(automodConfig.windowMs) : undefined,
          linkWhitelist:
            typeof automodConfig?.linkWhitelist === 'string'
              ? automodConfig.linkWhitelist
                  .split(',')
                  .map((s: string) => s.trim())
                  .filter(Boolean)
              : undefined,
          spamTimeoutMinutes: automodConfig?.spamTimeoutMinutes ? Number(automodConfig.spamTimeoutMinutes) : undefined,
          deleteLinks: automodConfig?.deleteLinks !== undefined ? automodConfig.deleteLinks === 'true' || automodConfig.deleteLinks === true : undefined
        };
  parsedAutomod.customBadwords = normalizeArray(automodConfig?.customBadwords);
  parsedAutomod.whitelistRoles = normalizeArray(automodConfig?.whitelistRoles);
  parsedAutomod.logChannelId = automodConfig?.logChannelId ?? logChannelId ?? parsedLogging.logChannelId;
  parsedAutomod.loggingConfig = parsedLogging;
  parsedAutomod.statuspageEnabled =
    typeof statuspageEnabled === 'string' ? statuspageEnabled === 'true' : statuspageEnabled ?? (current as any).statuspageEnabled;
  parsedAutomod.statuspageConfig = statuspageConfig ?? (current as any).statuspageConfig;
  const normalizedWelcomeEnabled = typeof welcomeEnabled === 'string' ? welcomeEnabled === 'true' : welcomeEnabled;
  const parsedWelcome = {
    enabled: welcomeConfig?.enabled ?? normalizedWelcomeEnabled ?? (current as any).welcomeConfig?.enabled ?? false,
    channelId: welcomeConfig?.channelId ?? (current as any).welcomeConfig?.channelId ?? (current as any).welcomeChannelId ?? undefined,
    embedTitle: welcomeConfig?.embedTitle ?? (current as any).welcomeConfig?.embedTitle,
    embedDescription: welcomeConfig?.embedDescription ?? (current as any).welcomeConfig?.embedDescription,
    embedColor: welcomeConfig?.embedColor ?? (current as any).welcomeConfig?.embedColor,
    embedFooter: welcomeConfig?.embedFooter ?? (current as any).welcomeConfig?.embedFooter,
    embedThumbnail: welcomeConfig?.embedThumbnail ?? (current as any).welcomeConfig?.embedThumbnail,
    embedImage: welcomeConfig?.embedImage ?? (current as any).welcomeConfig?.embedImage,
    embedThumbnailData: welcomeConfig?.embedThumbnailData ?? (current as any).welcomeConfig?.embedThumbnailData,
    embedImageData: welcomeConfig?.embedImageData ?? (current as any).welcomeConfig?.embedImageData
  };
  parsedAutomod.welcomeConfig = parsedWelcome;

  const parsedDynamicVoice = {
    lobbyChannelId: dynamicVoiceConfig?.lobbyChannelId ?? (current as any).dynamicVoiceConfig?.lobbyChannelId,
    categoryId: dynamicVoiceConfig?.categoryId ?? (current as any).dynamicVoiceConfig?.categoryId,
    template: dynamicVoiceConfig?.template ?? (current as any).dynamicVoiceConfig?.template,
    userLimit: dynamicVoiceConfig?.userLimit ?? (current as any).dynamicVoiceConfig?.userLimit,
    bitrate: dynamicVoiceConfig?.bitrate ?? (current as any).dynamicVoiceConfig?.bitrate
  };

  const existingBirthday = (current as any).birthdayConfig || {};
  const sendHourVal = birthdayConfig?.sendHour ?? existingBirthday.sendHour;
  const parsedBirthday = {
    enabled:
      birthdayConfig?.enabled ??
      (typeof birthdayEnabled === 'string' ? birthdayEnabled === 'true' : birthdayEnabled ?? (current as any).birthdayEnabled),
    channelId: birthdayConfig?.channelId ?? existingBirthday.channelId,
    sendHour: Number.isFinite(Number(sendHourVal)) ? Math.min(23, Math.max(0, Number(sendHourVal))) : existingBirthday.sendHour,
    messageTemplate: birthdayConfig?.messageTemplate ?? existingBirthday.messageTemplate
  };

  const existingReactionRoles = (current as any).reactionRolesConfig || {};
  const parsedReactionRoles = {
    enabled:
      reactionRolesConfig?.enabled ??
      (typeof reactionRolesEnabled === 'string'
        ? reactionRolesEnabled === 'true'
        : reactionRolesEnabled ?? (current as any).reactionRolesEnabled),
    channelId: reactionRolesConfig?.channelId ?? existingReactionRoles.channelId
  };

  const parsedRegister = {
    enabled:
      registerConfig?.enabled ??
      (typeof registerEnabled === 'string' ? registerEnabled === 'true' : registerEnabled ?? (current as any).registerEnabled),
    reviewChannelId: registerConfig?.reviewChannelId ?? (current as any).registerConfig?.reviewChannelId,
    notifyRoleIds:
      Array.isArray(registerConfig?.notifyRoleIds) && registerConfig?.notifyRoleIds.length
        ? registerConfig.notifyRoleIds
        : typeof registerConfig?.notifyRoleIds === 'string'
        ? registerConfig.notifyRoleIds
            .split(',')
            .map((s: string) => s.trim())
            .filter(Boolean)
        : (current as any).registerConfig?.notifyRoleIds || []
  };

  const updated = await settingsStore.set(guildId, {
    welcomeChannelId: welcomeChannelId ?? undefined,
    logChannelId: logChannelId ?? undefined,
    automodEnabled: typeof automodEnabled === 'string' ? automodEnabled === 'true' : automodEnabled,
    automodConfig: parsedAutomod,
    welcomeConfig: parsedWelcome,
    loggingConfig: parsedLogging,
    levelingEnabled: typeof levelingEnabled === 'string' ? levelingEnabled === 'true' : levelingEnabled,
    ticketsEnabled: typeof ticketsEnabled === 'string' ? ticketsEnabled === 'true' : ticketsEnabled,
    musicEnabled: typeof musicEnabled === 'string' ? musicEnabled === 'true' : musicEnabled,
    dynamicVoiceEnabled: typeof dynamicVoiceEnabled === 'string' ? dynamicVoiceEnabled === 'true' : dynamicVoiceEnabled,
    dynamicVoiceConfig: parsedDynamicVoice,
    supportRoleId: supportRoleId ?? undefined,
    statuspageEnabled: typeof statuspageEnabled === 'string' ? statuspageEnabled === 'true' : statuspageEnabled,
    statuspageConfig: parsedAutomod.statuspageConfig,
    eventsEnabled: typeof eventsEnabled === 'string' ? eventsEnabled === 'true' : eventsEnabled,
    birthdayEnabled: parsedBirthday.enabled,
    birthdayConfig: parsedBirthday,
    reactionRolesEnabled: parsedReactionRoles.enabled,
    reactionRolesConfig: parsedReactionRoles,
    registerEnabled: parsedRegister.enabled,
    registerConfig: parsedRegister,
    serverStatsEnabled: typeof serverStatsEnabled === 'string' ? serverStatsEnabled === 'true' : serverStatsEnabled,
    serverStatsConfig: serverStatsConfig
  });
  // Live update logging target
  context.logging = new LoggingService(updated.logChannelId);
  const { setLoggingAdmin } = await import('../../services/loggingService');
  setLoggingAdmin(context.admin);
  res.json({ ok: true, settings: updated });
});

export default router;
