import { Collection, Message } from 'discord.js';
import { logger } from '../utils/logger';
import { GuildSettings } from '../config/state';
import { LoggingService } from './loggingService';

export interface AutomodConfig {
  spamThreshold?: number;
  windowMs?: number;
  linkWhitelist?: string[];
  spamTimeoutMinutes?: number;
  deleteLinks?: boolean;
  badWordFilter?: boolean;
  capsFilter?: boolean;
  customBadwords?: string[];
  whitelistRoles?: string[];
  logChannelId?: string;
  loggingConfig?: {
    logChannelId?: string;
    categories?: {
      automodActions?: boolean;
    };
  };
}

export class AutoModService {
  private spamTracker = new Collection<string, { count: number; lastMessage: number }>();
  private spamThreshold = 5;
  private windowMs = 7000;
  private defaults: AutomodConfig = {
    spamThreshold: 5,
    windowMs: 7000,
    linkWhitelist: [],
    spamTimeoutMinutes: 10,
    deleteLinks: true,
    badWordFilter: true,
    capsFilter: false,
    customBadwords: [],
    whitelistRoles: []
  };
  private defaultBadwords = ['badword', 'spamword'];

  constructor(private logging?: LoggingService, private linkFilterEnabled = true, private antiSpamEnabled = true) {}

  public async checkMessage(message: Message, cfg?: AutomodConfig | GuildSettings) {
    if (message.author.bot || message.webhookId) return;
    if (!message.inGuild()) return;
    const guildConfig = (cfg as GuildSettings)?.automodConfig ? (cfg as GuildSettings).automodConfig : cfg;
    const config = { ...this.defaults, ...(guildConfig ?? {}) };
    const member = message.member;

    if (member?.roles.cache.size && Array.isArray(config.whitelistRoles) && config.whitelistRoles.length) {
      const allowed = member.roles.cache.some((r) => config.whitelistRoles!.includes(r.id));
      if (allowed) return;
    }

    if (this.linkFilterEnabled && config.deleteLinks !== false && this.containsLink(message.content, config.linkWhitelist)) {
      await this.deleteMessageWithReason(message, `${message.author}, Links sind hier nicht erlaubt.`);
      const reason = `Link gefunden (nicht freigegeben)${config.linkWhitelist?.length ? ` | Whitelist: ${config.linkWhitelist.join(', ')}` : ''}`;
      logger.info(`Deleted link from ${message.author.tag}`);
      await this.logAutomodAction(message, config, 'link_filter', reason);
      return true;
    }

    if (config.badWordFilter !== false && this.containsBadword(message.content, config.customBadwords)) {
      await this.deleteMessageWithReason(message, `${message.author}, bitte auf deine Wortwahl achten.`);
      await this.logAutomodAction(message, config, 'badword', 'Badword erkannt', message.content);
      return true;
    }

    if (config.capsFilter) {
      const letters = message.content.replace(/[^a-zA-Z]/g, '');
      const upper = letters.replace(/[^A-Z]/g, '');
      if (letters.length >= 10 && upper.length / letters.length > 0.7) {
        await this.deleteMessageWithReason(message, `${message.author}, bitte weniger Capslock nutzen.`);
        const ratio = Math.round((upper.length / letters.length) * 100);
        await this.logAutomodAction(message, config, 'capslock', `Caps Anteil ${ratio}%`, message.content);
        return true;
      }
    }

    if (this.antiSpamEnabled) {
      const now = Date.now();
      const tracker = this.spamTracker.get(message.author.id) ?? { count: 0, lastMessage: now };
      if (now - tracker.lastMessage < (config.windowMs ?? this.windowMs)) {
        tracker.count += 1;
      } else {
        tracker.count = 1;
      }
      tracker.lastMessage = now;
      this.spamTracker.set(message.author.id, tracker);

      const threshold = config.spamThreshold ?? this.spamThreshold;
      if (tracker.count >= threshold) {
        const timeoutMs = (config.spamTimeoutMinutes ?? this.defaults.spamTimeoutMinutes!) * 60 * 1000;
        message.member?.timeout(timeoutMs, 'Automod: Spam').catch(() => undefined);
        await this.deleteMessageWithReason(message, `${message.author}, bitte langsamer schreiben (Spam-Schutz).`);
        logger.warn(`Timed out ${message.author.tag} for spam`);
        this.spamTracker.delete(message.author.id);
        const reason = `Spam erkannt (${tracker.count}/${threshold} Nachrichten innerhalb ${config.windowMs ?? this.windowMs}ms)`;
        await this.logAutomodAction(message, config, 'spam', reason);
        return true;
      }
    }
    return false;
  }

  private containsBadword(content: string, custom: string[] = []) {
    const combined = [...this.defaultBadwords, ...(custom || [])]
      .map((w) => w?.toString().trim().toLowerCase())
      .filter(Boolean);
    if (!combined.length) return false;
    const lower = content.toLowerCase();
    return combined.some((w) => {
      // Try to match word boundaries first, fall back to substring to remain permissive
      const escaped = w.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
      const regex = new RegExp(`\\b${escaped}\\b`, 'i');
      return regex.test(lower) || lower.includes(w);
    });
  }

  private containsLink(content: string, whitelist: string[] = []) {
    const normalized = whitelist.map((w) => w.toLowerCase()).filter(Boolean);
    // Match common link formats, even without protocol
    const match = /(https?:\/\/[^\s]+|discord\.gg\/[^\s]+|www\.[^\s]+|[a-z0-9.-]+\.[a-z]{2,}\/?[^\s]*)/i.exec(content);
    if (!match) return false;
    const url = match[0].toLowerCase();
    return !normalized.some((w) => url.includes(w));
  }

  private async deleteMessageWithReason(message: Message, response: string) {
    await message.delete().catch(() => undefined);
    await message.channel
      .send({ content: response })
      .then((m) => setTimeout(() => m.delete().catch(() => undefined), 5000))
      .catch(() => undefined);
  }

  private async logAutomodAction(message: Message, config: AutomodConfig, action: string, reason: string, content?: string) {
    try {
      const guild = message.guild;
      if (!guild) return;
      if (this.logging) {
        this.logging.logAutomodAction(guild, {
          userTag: message.author.tag,
          userId: message.author.id,
          action,
          reason,
          content,
          channel: guild.channels.cache.get(message.channelId) ?? null,
          messageUrl: message.url
        });
        return;
      }
      const loggingCfg = config.loggingConfig || {};
      const flags = loggingCfg.categories || {};
      if (flags.automodActions === false) return;
      const channelId = loggingCfg.logChannelId || config.logChannelId;
      if (!channelId) return;
      const channel = await guild.channels.fetch(channelId).catch(() => null);
      if (!channel || !channel.isTextBased()) return;
      const body = `[Automod] ${action} by ${message.author.tag} | ${reason}${content ? ` | ${content.slice(0, 1800)}` : ''}`;
      await channel.send({ content: body });
    } catch (err) {
      logger.error('Automod log failed', err);
    }
  }
}
