const bcrypt = require('bcryptjs');
const {pool} = require('../config/db');
const {env} = require('../config/env');
const {createRefreshToken, hashToken, signAccessToken} = require('../utils/tokens');
const {getUserFrontendPermissions} = require('./permissionService');

function mapUser(row, permissions) {
  return {
    id: String(row.id),
    name: row.name,
    username: row.username,
    role: row.role_name,
    shopId: String(row.shop_id),
    permissions,
  };
}

async function buildSession(userRow, req) {
  const token = signAccessToken(userRow);
  const refreshTokenPair = createRefreshToken();
  const expiresAt = new Date(Date.now() + env.jwt.refreshTokenExpiresDays * 24 * 60 * 60 * 1000);
  const permissions = await getUserFrontendPermissions(userRow.id, userRow.role_name);

  await pool.execute(
    `INSERT INTO user_refresh_tokens (user_id, token_hash, device_name, ip_address, expires_at)
     VALUES (?, ?, ?, ?, ?)`,
    [userRow.id, refreshTokenPair.hash, req.headers['user-agent'] || null, req.ip || null, expiresAt],
  );

  await pool.execute('UPDATE users SET last_login_at = NOW() WHERE id = ?', [userRow.id]);

  return {
    token,
    refreshToken: refreshTokenPair.token,
    user: mapUser(userRow, permissions),
  };
}

async function login(payload, req) {
  const [rows] = await pool.execute(
    `SELECT u.*, r.role_name
     FROM users u
     INNER JOIN roles r ON r.id = u.role_id
     WHERE u.username = ? AND u.deleted_at IS NULL
     LIMIT 1`,
    [payload.username],
  );

  if (!rows.length || rows[0].status !== 'Active') {
    const error = new Error('Invalid login');
    error.statusCode = 401;
    throw error;
  }

  const isValidPassword = await bcrypt.compare(payload.password, rows[0].password_hash);
  if (!isValidPassword) {
    const error = new Error('Invalid login');
    error.statusCode = 401;
    throw error;
  }

  return buildSession(rows[0], req);
}

async function refresh(refreshToken, req) {
  const [rows] = await pool.execute(
    `SELECT rt.*, u.shop_id, u.username, u.name, u.status, r.role_name
     FROM user_refresh_tokens rt
     INNER JOIN users u ON u.id = rt.user_id
     INNER JOIN roles r ON r.id = u.role_id
     WHERE rt.token_hash = ? AND rt.revoked_at IS NULL AND rt.expires_at > NOW()
     LIMIT 1`,
    [hashToken(refreshToken)],
  );

  if (!rows.length || rows[0].status !== 'Active') {
    const error = new Error('Invalid refresh token');
    error.statusCode = 401;
    throw error;
  }

  await pool.execute('UPDATE user_refresh_tokens SET revoked_at = NOW() WHERE id = ?', [rows[0].id]);
  return buildSession(rows[0], req);
}

async function logout(userId, refreshToken) {
  if (refreshToken) {
    await pool.execute('UPDATE user_refresh_tokens SET revoked_at = NOW() WHERE user_id = ? AND token_hash = ?', [
      userId,
      hashToken(refreshToken),
    ]);
    return;
  }

  await pool.execute('UPDATE user_refresh_tokens SET revoked_at = NOW() WHERE user_id = ? AND revoked_at IS NULL', [userId]);
}

module.exports = {login, logout, refresh};
