// messageCreate orchestration: allowlist bypass -> invite link -> banned word
// -> spam/mass-mention/mass-emoji. Invite/spam triggers always delete + warn
// (no severity tiers for those, unlike the word filter) — kept simple per the
// spec's "start simple" guidance. Filter-triggered mutes use a fixed 10-minute
// duration; per-severity-configurable durations are a future refinement.
const filterCache = require('../filter/filterCache')
const { findMatch } = require('../filter/normalize')
const inviteFilter = require('../filter/inviteFilter')
const spamFilter = require('../filter/spamFilter')
const warnings = require('../model/warnings')
const filterHits = require('../model/filterHits')
const spamHits = require('../model/spamHits')
const modLog = require('./modLog')
const createLogger = require('../utils/logger')

const log = createLogger('filter')

const FILTER_MUTE_SECONDS = 600 // 10 minutes

function botActor(client) {
  return { id: client.user.id, tag: client.user.tag }
}

// Dashboard event capture (Phase 6b). Best-effort — recording a hit must never
// break the moderation action it accompanies, so failures are swallowed+logged.
async function recordFilterHit(message, hitType, matched, actionTaken) {
  try {
    await filterHits.record({
      guildId: message.guildId,
      hitType,
      discordUserId: message.author.id,
      username: message.author.tag,
      channelId: message.channelId,
      matched,
      actionTaken,
    })
  } catch (err) {
    log.warn('filter hit record failed', { message: err.message })
  }
}

async function recordSpamHit(message, spamType) {
  try {
    await spamHits.record({
      guildId: message.guildId,
      spamType,
      discordUserId: message.author.id,
      username: message.author.tag,
      channelId: message.channelId,
    })
  } catch (err) {
    log.warn('spam hit record failed', { message: err.message })
  }
}

// Which spam rule tripped (for the spam_hits row). isRateLimited has a side
// effect (records this message's timestamp) so it must be evaluated first, and
// exactly once — mirroring the original OR-order.
function detectSpam(message) {
  if (spamFilter.isRateLimited(message.guildId, message.author.id)) return 'rate_limit'
  if (spamFilter.isMassMention(message)) return 'mass_mention'
  if (spamFilter.isMassEmoji(message.content)) return 'mass_emoji'
  return null
}

async function isBypassed(message, cache) {
  if (cache.allowChannels.has(message.channelId)) return true
  const memberRoles = message.member ? message.member.roles.cache : null
  return Boolean(memberRoles && [...memberRoles.keys()].some((id) => cache.allowRoles.has(id)))
}

async function applyWarnAction(message, reason) {
  const staff = botActor(message.client)
  await warnings.add({
    guildId: message.guildId,
    targetUserId: message.author.id,
    targetTag: message.author.tag,
    staffUserId: staff.id,
    staffTag: staff.tag,
    reason,
  })
  await modLog.record({ client: message.client, guildId: message.guildId, actionType: 'warn', target: message.author, staffUser: staff, reason })
}

async function applyMuteAction(message, reason) {
  const staff = botActor(message.client)
  if (message.member && message.member.moderatable) {
    await message.member.timeout(FILTER_MUTE_SECONDS * 1000, reason)
  }
  await modLog.record({
    client: message.client,
    guildId: message.guildId,
    actionType: 'mute',
    target: message.author,
    staffUser: staff,
    reason,
    durationSeconds: FILTER_MUTE_SECONDS,
  })
}

async function handleMessageCreate(message) {
  if (message.author.bot || !message.guildId) return

  try {
    const cache = await filterCache.getOrLoad(message.guildId)
    if (await isBypassed(message, cache)) return

    const foreignCode = await inviteFilter.foreignInviteCode(message)
    if (foreignCode) {
      await message.delete().catch(() => {})
      await recordFilterHit(message, 'invite', foreignCode, 'warn')
      await applyWarnAction(message, 'Posted a Discord invite link')
      return
    }

    const match = findMatch(message.content, cache.words)
    if (match) {
      await message.delete().catch(() => {})
      await recordFilterHit(message, 'word', match.word, match.severity)
      if (match.severity === 'mute') await applyMuteAction(message, `Filtered word: ${match.word}`)
      else if (match.severity === 'warn') await applyWarnAction(message, `Filtered word: ${match.word}`)
      return
    }

    const spamType = detectSpam(message)
    if (spamType) {
      await message.delete().catch(() => {})
      await recordSpamHit(message, spamType)
      await applyWarnAction(message, 'Automated spam detection (rate limit / mass mention / mass emoji)')
    }
  } catch (err) {
    log.error('messageFilter failed', { message: err.message })
  }
}

module.exports = { handleMessageCreate }
