const crypto = require('crypto');
const bcrypt = require('bcryptjs');
const db = require('../db');
const config = require('../config');
const { AppError } = require('../utils/helpers');

async function createOtp(mobile, purpose) {
  const code = String(crypto.randomInt(0, 1000000)).padStart(6, '0');
  await db.query(
    `INSERT INTO otp_codes (mobile, code, purpose, expires_at)
     VALUES (?,?,?, DATE_ADD(NOW(), INTERVAL 10 MINUTE))`,
    [mobile, code, purpose],
  );
  return code;
}

async function consumeOtp(mobile, code, purpose) {
  const [rows] = await db.query(
    `SELECT id FROM otp_codes
     WHERE mobile = ? AND code = ? AND purpose = ? AND used = 0 AND expires_at > NOW()
     ORDER BY id DESC LIMIT 1`,
    [mobile, code, purpose],
  );
  if (!rows[0]) throw new AppError(400, 'Invalid or expired OTP');
  await db.query('UPDATE otp_codes SET used = 1 WHERE id = ?', [rows[0].id]);
}

async function createSession(user) {
  const token = crypto.randomBytes(32).toString('hex');
  await db.query('INSERT INTO sessions (token, user_id) VALUES (?,?)', [token, user.id]);
  return {
    token,
    user: { id: user.id, name: user.name, mobile: user.mobile },
  };
}

async function register({ name, mobile, password }) {
  if (!name || !/^[0-9]{10}$/.test(mobile || '') || !password || String(password).length < 6) {
    throw new AppError(400, 'Name, 10-digit mobile and password (6+ chars) required');
  }

  const [existing] = await db.query('SELECT id FROM users WHERE mobile = ?', [mobile]);
  if (existing[0]) throw new AppError(409, 'Mobile already registered');

  const hash = await bcrypt.hash(password, 10);
  await db.query(
    'INSERT INTO users (name, mobile, password_hash, mobile_verified_at) VALUES (?,?,?,NULL)',
    [name, mobile, hash],
  );

  const otp = await createOtp(mobile, 'register');
  const result = { ok: true, otp_required: true, message: 'OTP sent' };
  if (config.otpDebug) result.debug_otp = otp;
  return result;
}

async function login({ mobile, password }) {
  const [rows] = await db.query('SELECT * FROM users WHERE mobile = ?', [mobile]);
  const user = rows[0];
  if (!user || !(await bcrypt.compare(password || '', user.password_hash))) {
    throw new AppError(401, 'Invalid mobile or password');
  }

  // OTP only on first login / until the mobile is verified once.
  if (user.mobile_verified_at) {
    return { ok: true, otp_required: false, ...(await createSession(user)) };
  }

  const otp = await createOtp(mobile, 'login');
  const result = { ok: true, otp_required: true, message: 'OTP sent', purpose: 'login' };
  if (config.otpDebug) result.debug_otp = otp;
  return result;
}

async function verifyOtp({ mobile, code, purpose }) {
  await consumeOtp(mobile, String(code || ''), purpose || 'register');

  const [rows] = await db.query('SELECT * FROM users WHERE mobile = ?', [mobile]);
  if (!rows[0]) throw new AppError(404, 'Account not found');

  await db.query(
    'UPDATE users SET mobile_verified_at = COALESCE(mobile_verified_at, NOW()) WHERE id = ?',
    [rows[0].id],
  );

  return { ok: true, otp_required: false, ...(await createSession(rows[0])) };
}

async function getProfile(userId) {
  const [users] = await db.query('SELECT id, name, mobile FROM users WHERE id = ?', [userId]);
  const [subs] = await db.query(
    `SELECT plan, starts_at, ends_at FROM subscriptions
     WHERE user_id = ? ORDER BY id DESC LIMIT 1`,
    [userId],
  );
  return { user: users[0], subscription: subs[0] || null };
}

module.exports = { register, login, verifyOtp, getProfile };
