// Player self-service moderation appeals (Phase 6c). A player appeals one of
// their OWN ban/mute mod_actions: they see the actions they're allowed to appeal
// (ban/mute with no active appeal), submit one, view their appeals, and withdraw
// one that hasn't been resolved yet. Ownership is proven by matching the
// action's target_user_id against the caller's linked Discord identity — the
// same (provider='discord', subject=<snowflake>) link the SSO flow writes.
// Mounted behind the /player group gate — authenticated only, no role restriction
// (see player/index.js); ownership is enforced per handler.
const appeals = require('../../../model/appeals/appeals.model')
const appealsDb = require('../../../model/appeals/appeals.db')
const { isAppealableType, isTerminal } = require('../../../model/appeals/appeals.pure')
const userIdentities = require('../../../model/userIdentities/userIdentities.model')

const log = require('../../../utils/logger')('player-appeals')

// The caller's linked Discord snowflake, or null if they have no Discord
// identity linked. mod_actions are keyed by this snowflake.
async function callerDiscordSubject(userId) {
  const identities = await userIdentities.listForUser(userId)
  const discord = identities.find((i) => i.provider === 'discord')
  return discord ? discord.subject : null
}

// GET /player/appeals — the caller's own appeals, newest first.
async function listMine(req, res) {
  try {
    return res.json(await appeals.listMine(req.user.id))
  } catch (err) {
    log.error('listMine failed', { error: err.message })
    return res.status(500).json({ message: 'Internal Server Error' })
  }
}

// GET /player/appeals/eligible — the caller's ban/mute actions with no active
// appeal. If the caller has no linked Discord account we return [] (not an
// error) so the UI can show a "link your Discord account" hint instead.
async function listEligible(req, res) {
  try {
    const subject = await callerDiscordSubject(req.user.id)
    if (!subject) return res.json([])
    return res.json(await appealsDb.eligibleActions(subject))
  } catch (err) {
    log.error('listEligible failed', { error: err.message })
    return res.status(500).json({ message: 'Internal Server Error' })
  }
}

// POST /player/appeals — open an appeal for one of the caller's own actions.
async function create(req, res) {
  try {
    const modActionId = Number(req.body.mod_action_id)
    const submittedText = req.body.submitted_text

    const action = await appealsDb.getModAction(modActionId)
    if (!action) return res.status(404).json({ message: 'Mod action not found' })

    const subject = await callerDiscordSubject(req.user.id)
    if (!subject || String(action.target_user_id) !== String(subject)) {
      return res.status(403).json({ message: 'This action is not yours to appeal' })
    }

    if (!isAppealableType(action.action_type)) {
      return res.status(400).json({ message: 'Only bans and mutes can be appealed' })
    }

    const active = await appeals.activeForAction(modActionId)
    if (active) return res.status(409).json({ message: 'An appeal for this action is already open' })

    const appeal = await appeals.submit({
      modActionId,
      discordUserId: subject,
      actionType: action.action_type,
      userId: req.user.id,
      submittedText,
    })
    return res.status(201).json(appeal)
  } catch (err) {
    log.error('create failed', { error: err.message })
    return res.status(500).json({ message: 'Internal Server Error' })
  }
}

// POST /player/appeals/:id/withdraw — the owner withdraws a non-resolved appeal.
async function withdraw(req, res) {
  try {
    const appeal = await appeals.getById(Number(req.params.id))
    // 404 for both "no such appeal" and "not the caller's" — never confirm the
    // existence of another player's appeal.
    if (!appeal || appeal.user_id !== req.user.id) {
      return res.status(404).json({ message: 'Appeal not found' })
    }
    if (isTerminal(appeal.status)) {
      return res.status(409).json({ message: 'This appeal is already resolved' })
    }
    return res.json(await appeals.withdraw(appeal.id))
  } catch (err) {
    log.error('withdraw failed', { error: err.message })
    return res.status(500).json({ message: 'Internal Server Error' })
  }
}

module.exports = { listMine, listEligible, create, withdraw }
