// Best-effort invite-usage attribution (Phase 6b). Discord doesn't tell you
// which invite a member used, so the standard approach is to keep a cache of
// each invite's use-count and, on guildMemberAdd, re-fetch and find the one
// whose count went up. Requires the GuildInvites intent + Manage Guild (the bot
// already creates/deletes invites, so it has the permission). All calls are
// best-effort: any failure just yields a null attribution and the join is still
// recorded. Vanity-URL and bot-added joins are inherently unattributable.
const createLogger = require('../utils/logger')

const log = createLogger('invites')

// guildId -> Map<inviteCode, uses>
const cache = new Map()

async function snapshot(guild) {
  const map = new Map()
  const invites = await guild.invites.fetch()
  for (const inv of invites.values()) map.set(inv.code, inv.uses || 0)
  return map
}

// Populate the cache for a guild (call once the client is ready).
async function prime(client, guildId) {
  try {
    const guild = client.guilds.cache.get(guildId) || (await client.guilds.fetch(guildId))
    cache.set(guildId, await snapshot(guild))
    log.info('invite cache primed', { guildId, count: cache.get(guildId).size })
  } catch (err) {
    log.warn('invite cache prime failed (missing Manage Guild / GuildInvites?)', { message: err.message })
  }
}

function onInviteCreate(invite) {
  if (!invite.guild) return
  const g = cache.get(invite.guild.id) || new Map()
  g.set(invite.code, invite.uses || 0)
  cache.set(invite.guild.id, g)
}

function onInviteDelete(invite) {
  if (!invite.guild) return
  const g = cache.get(invite.guild.id)
  if (g) g.delete(invite.code)
}

// Diff current invite uses against the cached snapshot to find which invite the
// joining member used, then refresh the cache. Returns { code, inviterId,
// inviterTag } with nulls when it can't be determined.
async function attribute(member) {
  const empty = { code: null, inviterId: null, inviterTag: null }
  try {
    const guild = member.guild
    const before = cache.get(guild.id) || new Map()
    const current = await guild.invites.fetch()

    let found = empty
    for (const inv of current.values()) {
      const prev = before.get(inv.code) || 0
      if ((inv.uses || 0) > prev && found === empty) {
        found = { code: inv.code, inviterId: inv.inviter?.id || null, inviterTag: inv.inviter?.tag || null }
      }
    }

    const next = new Map()
    for (const inv of current.values()) next.set(inv.code, inv.uses || 0)
    cache.set(guild.id, next)
    return found
  } catch (err) {
    log.warn('invite attribution failed', { message: err.message })
    return empty
  }
}

module.exports = { prime, onInviteCreate, onInviteDelete, attribute }
