const { query } = require('../../utils/db')

// SQL for the mobile_refresh_tokens table. Tokens are stored only as sha256
// hashes (token_hash); the raw refresh token never touches the database.

// Insert a new refresh-token row. expiresAt is a JS Date (or ms epoch). last_used_at
// is seeded to now: with single-use rotation each login/refresh inserts a fresh
// row, so the current active row's timestamp IS the session's last activity.
async function insert({ userId, tokenHash, deviceHash = null, deviceName = null, userAgent = null, expiresAt }) {
  const res = await query(
    `INSERT INTO mobile_refresh_tokens (user_id, token_hash, device_hash, device_name, user_agent, expires_at, last_used_at)
     VALUES (?, ?, ?, ?, ?, ?, NOW())`,
    [userId, tokenHash, deviceHash, deviceName, userAgent, new Date(expiresAt)],
  )
  return res.insertId
}

// List a user's currently-active (unrevoked, unexpired) sessions — one row per
// live device, newest first. Never returns the token hash. For the "Active
// Devices" self-service surface.
async function listActiveForUser(userId) {
  return query(
    `SELECT id, device_name, device_hash, user_agent, created_at, last_used_at, expires_at
       FROM mobile_refresh_tokens
      WHERE user_id = ? AND revoked_at IS NULL AND expires_at > NOW()
      ORDER BY last_used_at DESC, created_at DESC`,
    [userId],
  )
}

// Revoke one of a user's sessions by row id (ownership-scoped so a user can only
// revoke their own). Idempotent; returns rows changed.
async function revokeByIdForUser(id, userId) {
  const res = await query(
    'UPDATE mobile_refresh_tokens SET revoked_at = NOW() WHERE id = ? AND user_id = ? AND revoked_at IS NULL',
    [id, userId],
  )
  return Number(res.affectedRows || 0)
}

// Look up a token by hash only if it is still usable: not revoked and not past
// its expiry. Returns the row (incl. user_id) or null.
async function findValidByHash(tokenHash) {
  const rows = await query(
    `SELECT * FROM mobile_refresh_tokens
     WHERE token_hash = ? AND revoked_at IS NULL AND expires_at > NOW()
     LIMIT 1`,
    [tokenHash],
  )
  return rows[0] || null
}

// Mark a single token revoked (idempotent — only affects a not-yet-revoked row).
// Returns the number of rows changed.
async function revokeByHash(tokenHash) {
  const res = await query(
    'UPDATE mobile_refresh_tokens SET revoked_at = NOW() WHERE token_hash = ? AND revoked_at IS NULL',
    [tokenHash],
  )
  return Number(res.affectedRows || 0)
}

// Revoke every active token for a user (logout-everywhere). Returns rows changed.
async function revokeAllForUser(userId) {
  const res = await query(
    'UPDATE mobile_refresh_tokens SET revoked_at = NOW() WHERE user_id = ? AND revoked_at IS NULL',
    [userId],
  )
  return Number(res.affectedRows || 0)
}

// Housekeeping: delete rows that are long dead (expired or revoked). Keeps the
// table from growing without bound. Returns rows removed.
async function pruneExpired() {
  const res = await query(
    'DELETE FROM mobile_refresh_tokens WHERE expires_at < NOW() OR revoked_at IS NOT NULL',
  )
  return Number(res.affectedRows || 0)
}

module.exports = {
  insert,
  listActiveForUser,
  revokeByIdForUser,
  findValidByHash,
  revokeByHash,
  revokeAllForUser,
  pruneExpired,
}
