import { Client, Guild, Invite, GuildMember } from 'discord.js';
import { prisma } from '../database';
import { logger } from '../utils/logger';

export class InviteService {
  /** In-process invite snapshot per guild for join attribution. */
  private static readonly inviteCache = new Map<string, string>();

  public static async cacheAllInvites(client: Client) {
    for (const [, guild] of client.guilds.cache) {
      await this.cacheGuildInvites(guild);
    }
    logger.info('InviteMaster: Finished caching all invites.');
  }

  public static async cacheGuildInvites(guild: Guild) {
    try {
      const invites = await guild.invites.fetch();
      const inviteData = invites.map(inv => ({
        code: inv.code,
        uses: inv.uses || 0
      }));
      this.inviteCache.set(guild.id, JSON.stringify(inviteData));
    } catch (error) {
      logger.error(`InviteMaster: Failed to cache invites for guild ${guild.id}:`, error);
    }
  }

  public static async handleInviteCreate(invite: Invite) {
    if (!invite.guild) return;
    logger.debug(`InviteMaster: New invite created: ${invite.code}`);
    await this.cacheGuildInvites(invite.guild as Guild);
  }

  public static async handleInviteDelete(invite: Invite) {
    if (!invite.guild) return;
    logger.debug(`InviteMaster: Invite deleted: ${invite.code}`);
    await this.cacheGuildInvites(invite.guild as Guild);
  }

  public static async handleMemberAdd(member: GuildMember) {
    const guild = member.guild;
    try {
      // Fetch current active invites
      const newInvites = await guild.invites.fetch();
      const cachedData = this.inviteCache.get(guild.id);
      
      let usedInvite: Invite | undefined;

      if (cachedData) {
        const cachedInvites: { code: string, uses: number }[] = JSON.parse(cachedData);
        
        // Find the invite where 'uses' has increased
        usedInvite = newInvites.find(inv => {
          const cached = cachedInvites.find(c => c.code === inv.code);
          return cached ? (inv.uses || 0) > cached.uses : false;
        });
      }

      // Update the cache immediately to account for this new join
      await this.cacheGuildInvites(guild);

      if (usedInvite) {
        logger.info(`InviteMaster: ${member.user.tag} joined using invite ${usedInvite.code}`);
        
        // Check DB for mapped role
        const inviteRole = await prisma.inviteRole.findFirst({
          where: {
            guildId: guild.id,
            inviteCode: usedInvite.code
          }
        });

        if (inviteRole) {
          const role = guild.roles.cache.get(inviteRole.roleId);
          if (role) {
            await member.roles.add(role);
            logger.info(`InviteMaster: Assigned role ${role.name} to ${member.user.tag}`);
          } else {
            logger.warn(`InviteMaster: Role ${inviteRole.roleId} mapped to invite ${usedInvite.code} not found.`);
          }
        }
      } else {
        logger.info(`InviteMaster: ${member.user.tag} joined but invite could not be determined (ex: Vanity URL).`);
      }
    } catch (error) {
      logger.error(`InviteMaster: Failed to handle member add tracking:`, error);
    }
  }
}
