const { query, one, transaction } = require('../lib/db')

/**
 * Find active binding for a user (bind_status=1)
 * Uses transaction connection when provided
 * @param {number} userId
 * @param {Object} [conn] - optional transaction connection
 * @returns {Promise<Object|null>}
 */
async function findActiveByUser(userId, conn) {
  if (conn) {
    const [rows] = await conn.execute(
      'SELECT device_id FROM bindings WHERE user_id = ? AND bind_status = 1 LIMIT 1',
      [userId]
    )
    return rows[0] || null
  }
  return one(
    'SELECT device_id FROM bindings WHERE user_id = :user_id AND bind_status = 1 LIMIT 1',
    { user_id: userId }
  )
}

/**
 * Check if a device exists and is not disabled (status <> 4)
 * Uses transaction connection when provided
 * @param {string} deviceId
 * @param {Object} [conn] - optional transaction connection
 * @returns {Promise<Object|null>}
 */
async function findDeviceExists(deviceId, conn) {
  if (conn) {
    const [rows] = await conn.execute(
      'SELECT * FROM devices WHERE device_id = ? AND status <> 4 LIMIT 1',
      [deviceId]
    )
    return rows[0] || null
  }
  return one(
    'SELECT * FROM devices WHERE device_id = :device_id AND status <> 4 LIMIT 1',
    { device_id: deviceId }
  )
}

/**
 * Cancel any existing pending bindings for a user (set bind_status=4)
 * @param {number} userId
 * @returns {Promise<Array>} query result
 */
async function cancelPending(userId) {
  return query(
    'UPDATE bindings SET bind_status = 4 WHERE user_id = :user_id AND bind_status = 3',
    { user_id: userId }
  )
}

/**
 * Create a pending binding request with 10-minute expiry
 * @param {number} userId
 * @param {string} deviceId
 * @param {string} bindToken
 * @param {Object} [conn] - optional transaction connection
 * @returns {Promise<void>}
 */
async function createPending(userId, deviceId, bindToken, conn) {
  if (conn) {
    await conn.execute(
      'INSERT INTO bindings (user_id, device_id, bind_token, bind_expires, bind_status, bind_time) VALUES (?, ?, ?, DATE_ADD(NOW(), INTERVAL 10 MINUTE), 3, NOW())',
      [userId, deviceId, bindToken]
    )
    return
  }
  await query(
    'INSERT INTO bindings (user_id, device_id, bind_token, bind_expires, bind_status, bind_time) VALUES (:user_id, :device_id, :bind_token, DATE_ADD(NOW(), INTERVAL 10 MINUTE), 3, NOW())',
    { user_id: userId, device_id: deviceId, bind_token: bindToken }
  )
}

/**
 * Confirm a pending binding by token, set bind_status=1.
 * Also ensures trial subscription if none active.
 * @param {number} userId
 * @param {string} deviceId
 * @param {string} bindToken
 * @returns {Promise<boolean>} true if confirmed, false if token invalid/expired
 */
async function confirmBind(userId, deviceId, bindToken) {
  return transaction(async conn => {
    const [rows] = await conn.execute(
      'SELECT binding_id FROM bindings WHERE user_id = ? AND device_id = ? AND bind_token = ? AND bind_status = 3 AND bind_expires > NOW() LIMIT 1',
      [userId, deviceId, bindToken]
    )
    if (rows.length === 0) return false
    await conn.execute(
      'UPDATE bindings SET bind_status = 1, bind_time = NOW() WHERE binding_id = ?',
      [rows[0].binding_id]
    )
    const subscriptionDao = require('./subscription.dao')
    await subscriptionDao.grantTrialIfEligible(userId, conn)
    return true
  })
}

/**
 * Unbind device(s) for a user
 * @param {number} userId
 * @param {string|null} deviceId - specific device or null for all active bindings
 * @returns {Promise<Array>} query result
 */
async function unbindByUser(userId, deviceId) {
  return query(
    'UPDATE bindings SET bind_status = 2, unbind_time = NOW() WHERE user_id = :user_id AND bind_status = 1 AND (:device_id IS NULL OR device_id = :device_id)',
    { user_id: userId, device_id: deviceId }
  )
}

/**
 * Get binding history for a device with user nicknames
 * @param {string} deviceId
 * @returns {Promise<Array>}
 */
async function getHistoryByDevice(deviceId) {
  return query(
    'SELECT b.*, u.nickname FROM bindings b LEFT JOIN users u ON u.user_id = b.user_id WHERE b.device_id = :device_id ORDER BY b.bind_time DESC',
    { device_id: deviceId }
  )
}

/**
 * Check if a user has an active binding to a specific device
 * @param {number} userId
 * @param {string} deviceId
 * @returns {Promise<Object|null>}
 */
async function findUserDeviceBinding(userId, deviceId) {
  return one(
    'SELECT binding_id FROM bindings WHERE user_id = :user_id AND device_id = :device_id AND bind_status = 1',
    { user_id: userId, device_id: deviceId }
  )
}

/**
 * Count active bindings for a user
 * @param {number} userId
 * @returns {Promise<number>}
 */
async function countActiveByUser(userId) {
  const rows = await query(
    'SELECT COUNT(*) AS total FROM bindings WHERE user_id = :user_id AND bind_status = 1',
    { user_id: userId }
  )
  return rows[0].total
}

async function closeExpiredPending() {
  return query(
    'UPDATE bindings SET bind_status = 4 WHERE bind_status = 3 AND bind_expires < NOW()'
  )
}

module.exports = {
  findActiveByUser,
  findDeviceExists,
  cancelPending,
  createPending,
  closeExpiredPending,
  confirmBind,
  unbindByUser,
  getHistoryByDevice,
  findUserDeviceBinding,
  countActiveByUser
}
