import { TextChannel, Guild, Message, GuildMember, User, EmbedBuilder, GuildChannel } from 'discord.js';
import { logger } from '../utils/logger';
import { settingsStore } from '../config/state';
import type { AdminService } from './adminService';

let adminSink: AdminService | null = null;
export const setLoggingAdmin = (admin: AdminService) => {
  adminSink = admin;
};

type LogCategory =
  | 'joinLeave'
  | 'messageEdit'
  | 'messageDelete'
  | 'automodActions'
  | 'ticketActions'
  | 'musicEvents'
  | 'system';

export class LoggingService {
  constructor(private fallbackLogChannelId?: string) {}

  private safeField(value?: string | null) {
    const text = (value ?? '').toString();
    const normalized = text.trim();
    if (!normalized.length) return '(leer)';
    return normalized.length > 1024 ? normalized.slice(0, 1021) + '...' : normalized;
  }

  logSystem(guild: Guild, message: string) {
    if (!this.shouldLog(guild, 'system')) return;
    const { channel } = this.resolve(guild);
    if (!channel) return;
    const embed = new EmbedBuilder().setTitle('System').setDescription(message).setColor(0xf97316).setTimestamp();
    channel.send({ embeds: [embed] }).catch((err) => logger.error('Failed to log system', err));
    adminSink?.pushGuildLog({
      guildId: guild.id,
      level: 'INFO',
      message,
      timestamp: Date.now(),
      category: 'system'
    });
  }

  private resolve(guild: Guild) {
    const cfg = settingsStore.get(guild.id);
    const loggingCfg = cfg?.loggingConfig || cfg?.automodConfig?.loggingConfig || {};
    const logChannelId = loggingCfg.logChannelId || cfg?.automodConfig?.logChannelId || cfg?.logChannelId || this.fallbackLogChannelId;
    const flags = loggingCfg.categories || {};
    const channel = logChannelId ? guild.channels.cache.get(logChannelId) : null;
    return { channel: channel && channel.type === 0 ? (channel as TextChannel) : null, flags };
  }

  private shouldLog(guild: Guild, category: LogCategory) {
    const { flags } = this.resolve(guild);
    if (flags[category] === false) return false;
    return true;
  }

  logMemberJoin(member: GuildMember) {
    if (!this.shouldLog(member.guild, 'joinLeave')) return;
    const { channel } = this.resolve(member.guild);
    if (!channel) return;
    const embed = new EmbedBuilder()
      .setTitle('Member joined')
      .setDescription(`${member.user.tag} ist dem Server beigetreten.`)
      .setColor(0x00ff99)
      .setTimestamp();
    channel.send({ embeds: [embed] }).catch((err) => logger.error('Failed to log join', err));
    adminSink?.pushGuildLog({ guildId: member.guild.id, level: 'INFO', message: 'Member joined: ' + member.user.tag, timestamp: Date.now(), category: 'joinLeave' });
  }

  logMemberLeave(member: GuildMember) {
    if (!this.shouldLog(member.guild, 'joinLeave')) return;
    const { channel } = this.resolve(member.guild);
    if (!channel) return;
    const embed = new EmbedBuilder()
      .setTitle('Member left')
      .setDescription(`${member.user.tag} hat den Server verlassen.`)
      .setColor(0xff9900)
      .setTimestamp();
    channel.send({ embeds: [embed] }).catch((err) => logger.error('Failed to log leave', err));
    adminSink?.pushGuildLog({ guildId: member.guild.id, level: 'WARN', message: 'Member left: ' + member.user.tag, timestamp: Date.now(), category: 'joinLeave' });
  }

  logMessageDelete(message: Message) {
    if (!message.guild) return;
    if (!this.shouldLog(message.guild, 'messageDelete')) return;
    const { channel } = this.resolve(message.guild);
    if (!channel) return;
    const embed = new EmbedBuilder()
      .setTitle('Nachricht geloescht')
      .setDescription(`Von: ${message.author?.tag ?? 'Unbekannt'}`)
      .addFields({ name: 'Inhalt', value: this.safeField(message.content) })
      .setColor(0xff0000)
      .setTimestamp();
    channel.send({ embeds: [embed] }).catch((err) => logger.error('Failed to log message delete', err));
    adminSink?.pushGuildLog({
      guildId: message.guild.id,
      level: 'WARN',
      message: 'Message deleted by ' + (message.author?.tag ?? 'Unknown'),
      timestamp: Date.now(),
      category: 'messageDelete'
    });
  }

  logMessageEdit(oldMessage: Message, newMessage: Message) {
    if (!oldMessage.guild) return;
    if (!this.shouldLog(oldMessage.guild, 'messageEdit')) return;
    const { channel } = this.resolve(oldMessage.guild);
    if (!channel) return;
    const embed = new EmbedBuilder()
      .setTitle('Nachricht bearbeitet')
      .setDescription(`Von: ${oldMessage.author?.tag ?? 'Unbekannt'}`)
      .addFields(
        { name: 'Alt', value: this.safeField(oldMessage.content) },
        { name: 'Neu', value: this.safeField(newMessage.content) }
      )
      .setColor(0xffff00)
      .setTimestamp();
    channel.send({ embeds: [embed] }).catch((err) => logger.error('Failed to log message edit', err));
    adminSink?.pushGuildLog({
      guildId: oldMessage.guild.id,
      level: 'INFO',
      message: 'Message edited by ' + (oldMessage.author?.tag ?? 'Unknown'),
      timestamp: Date.now(),
      category: 'messageEdit'
    });
  }

  logAction(user: User | GuildMember, action: string, reason?: string, guild?: Guild) {
    const resolvedGuild = guild ?? (user instanceof GuildMember ? user.guild : null);
    if (!resolvedGuild) return;
    if (!this.shouldLog(resolvedGuild, 'automodActions')) return;
    const { channel } = this.resolve(resolvedGuild);
    if (!channel) return;
    const embed = new EmbedBuilder()
      .setTitle('Moderation')
      .setDescription(`${user.tag} -> ${action}`)
      .addFields({ name: 'Grund', value: this.safeField(reason || 'Nicht angegeben') })
      .setColor(0x7289da)
      .setTimestamp();
    channel.send({ embeds: [embed] }).catch((err) => logger.error('Failed to log action', err));
    const guildId = resolvedGuild.id;
    if (guildId) {
      adminSink?.pushGuildLog({
        guildId,
        level: 'INFO',
        message: `Moderation action: ${action} (${user.tag})`,
        timestamp: Date.now(),
        category: 'automodActions'
      });
      adminSink?.trackGuildEvent(guildId, 'automod');
    }
  }

  logAutomodAction(guild: Guild, options: { userTag: string; userId: string; action: string; reason: string; content?: string; channel?: GuildChannel | null; messageUrl?: string }) {
    if (!this.shouldLog(guild, 'automodActions')) return;
    const { channel } = this.resolve(guild);
    if (!channel) return;
    const embed = new EmbedBuilder()
      .setTitle('Automod')
      .setDescription(`${options.userTag} (${options.userId}) -> ${options.action}`)
      .addFields(
        { name: 'Grund', value: this.safeField(options.reason) },
        { name: 'Kanal', value: options.channel ? `<#${options.channel.id}>` : 'Unbekannt' }
      )
      .setColor(0xff006e)
      .setTimestamp();
    if (options.content) {
      embed.addFields({ name: 'Nachricht', value: this.safeField(options.content) });
    }
    if (options.messageUrl) {
      embed.addFields({ name: 'Link', value: options.messageUrl });
    }
    channel.send({ embeds: [embed] }).catch((err) => logger.error('Failed to log automod action', err));
    adminSink?.pushGuildLog({
      guildId: guild.id,
      level: 'INFO',
      message: `Automod: ${options.action} (${options.userTag})`,
      timestamp: Date.now(),
      category: 'automodActions'
    });
    adminSink?.trackGuildEvent(guild.id, 'automod');
  }

  logRoleUpdate(member: GuildMember, added: string[], removed: string[]) {
    const guildId = member.guild.id;
    adminSink?.pushGuildLog({
      guildId,
      level: 'INFO',
      message: `Roles changed for ${member.user.tag}: +${added.length} / -${removed.length}`,
      timestamp: Date.now(),
      category: 'roles'
    });
  }
}
