/**
 * User Repository - Data access layer for users
 * Handles encryption/decryption of sensitive fields
 */

const { getDatabase } = require('../database/connection');
const { encrypt, decrypt } = require('../utils/encryption');
const crypto = require('crypto');

/**
 * Create a new user
 * @param {Object} userData - User data
 * @returns {Object} Created user
 */
function createUser(userData) {
  const db = getDatabase();
  if (!db) {
    throw new Error('Database not initialized');
  }
  
  const now = Date.now();
  const id = userData.id || crypto.randomUUID();
  
  // Encrypt sensitive fields
  const emailEncrypted = encrypt(userData.email);
  const nameEncrypted = userData.name ? encrypt(userData.name) : null;
  
  const stmt = db.prepare(`
    INSERT INTO users (
      id, email, email_encrypted, name, name_encrypted, password_hash,
      providers, email_verified, verification_token, verification_expires_at,
      plan, billing_status, billing_email, created_at, updated_at
    ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
  `);
  
  stmt.run(
    id,
    userData.email,
    emailEncrypted,
    userData.name || null,
    nameEncrypted,
    userData.passwordHash,
    JSON.stringify(userData.providers || []),
    userData.emailVerified ? 1 : 0,
    userData.verificationToken || null,
    userData.verificationExpiresAt || null,
    userData.plan || 'hobby',
    userData.billingStatus || 'active',
    userData.billingEmail || userData.email,
    now,
    now
  );
  
  return getUserById(id);
}

/**
 * Get user by ID
 * @param {string} userId - User ID
 * @returns {Object|null} User object or null
 */
function getUserById(userId) {
  const db = getDatabase();
  if (!db) {
    throw new Error('Database not initialized');
  }
  
  const stmt = db.prepare('SELECT * FROM users WHERE id = ?');
  const row = stmt.get(userId);
  
  return row ? deserializeUser(row) : null;
}

/**
 * Get user by email
 * @param {string} email - User email
 * @returns {Object|null} User object or null
 */
function getUserByEmail(email) {
  const db = getDatabase();
  if (!db) {
    throw new Error('Database not initialized');
  }
  
  const stmt = db.prepare('SELECT * FROM users WHERE email = ?');
  const row = stmt.get(email);
  
  return row ? deserializeUser(row) : null;
}

/**
 * Get user by verification token
 * @param {string} token - Verification token
 * @returns {Object|null} User object or null
 */
function getUserByVerificationToken(token) {
  const db = getDatabase();
  if (!db) {
    throw new Error('Database not initialized');
  }
  
  const stmt = db.prepare('SELECT * FROM users WHERE verification_token = ?');
  const row = stmt.get(token);
  
  return row ? deserializeUser(row) : null;
}

/**
 * Get user by reset token
 * @param {string} token - Reset token
 * @returns {Object|null} User object or null
 */
function getUserByResetToken(token) {
  const db = getDatabase();
  if (!db) {
    throw new Error('Database not initialized');
  }
  
  const stmt = db.prepare('SELECT * FROM users WHERE reset_token = ?');
  const row = stmt.get(token);
  
  return row ? deserializeUser(row) : null;
}

/**
 * Update user
 * @param {string} userId - User ID
 * @param {Object} updates - Fields to update
 * @returns {Object|null} Updated user
 */
function updateUser(userId, updates) {
  const db = getDatabase();
  if (!db) {
    throw new Error('Database not initialized');
  }
  
  const user = getUserById(userId);
  if (!user) {
    return null;
  }
  
  const sets = [];
  const values = [];
  
  // Handle regular fields
  const simpleFields = [
    'email', 'name', 'password_hash', 'email_verified',
    'verification_token', 'verification_expires_at',
    'reset_token', 'reset_expires_at', 'plan', 'billing_status',
    'billing_email', 'payment_method_last4', 'subscription_renews_at',
    'referred_by_affiliate_code', 'affiliate_attribution_at',
    'two_factor_enabled', 'last_login_at'
  ];
  
  simpleFields.forEach(field => {
    if (updates.hasOwnProperty(field)) {
      sets.push(`${field} = ?`);
      
      // Handle boolean fields
      if (field.includes('_verified') || field.includes('_enabled')) {
        values.push(updates[field] ? 1 : 0);
      } else {
        values.push(updates[field]);
      }
      
      // Handle encrypted fields
      if (field === 'email' && updates.email) {
        sets.push('email_encrypted = ?');
        values.push(encrypt(updates.email));
      } else if (field === 'name' && updates.name) {
        sets.push('name_encrypted = ?');
        values.push(encrypt(updates.name));
      }
    }
  });
  
  // Handle JSON fields
  if (updates.providers) {
    sets.push('providers = ?');
    values.push(JSON.stringify(updates.providers));
  }
  
  if (updates.affiliatePayouts) {
    sets.push('affiliate_payouts = ?');
    values.push(JSON.stringify(updates.affiliatePayouts));
  }
  
  // Handle encrypted 2FA secret
  if (updates.twoFactorSecret) {
    sets.push('two_factor_secret = ?');
    values.push(encrypt(updates.twoFactorSecret));
  }
  
  if (sets.length === 0) {
    return user;
  }
  
  // Add updated_at
  sets.push('updated_at = ?');
  values.push(Date.now());
  
  // Add userId for WHERE clause
  values.push(userId);
  
  const sql = `UPDATE users SET ${sets.join(', ')} WHERE id = ?`;
  const stmt = db.prepare(sql);
  stmt.run(...values);
  
  return getUserById(userId);
}

/**
 * Delete user
 * @param {string} userId - User ID
 * @returns {boolean} True if deleted
 */
function deleteUser(userId) {
  const db = getDatabase();
  if (!db) {
    throw new Error('Database not initialized');
  }
  
  const stmt = db.prepare('DELETE FROM users WHERE id = ?');
  const result = stmt.run(userId);
  
  return result.changes > 0;
}

/**
 * Get all users (with pagination)
 * @param {Object} options - Query options (limit, offset)
 * @returns {Array} Array of users
 */
function getAllUsers(options = {}) {
  const db = getDatabase();
  if (!db) {
    throw new Error('Database not initialized');
  }
  
  const limit = options.limit || 100;
  const offset = options.offset || 0;
  
  const stmt = db.prepare('SELECT * FROM users ORDER BY created_at DESC LIMIT ? OFFSET ?');
  const rows = stmt.all(limit, offset);
  
  return rows.map(deserializeUser);
}

/**
 * Count total users
 * @returns {number} Total user count
 */
function countUsers() {
  const db = getDatabase();
  if (!db) {
    throw new Error('Database not initialized');
  }
  
  const stmt = db.prepare('SELECT COUNT(*) as count FROM users');
  const result = stmt.get();
  
  return result.count;
}

/**
 * Deserialize user row from database
 * Converts database row to user object with decrypted fields
 * @param {Object} row - Database row
 * @returns {Object} User object
 */
function deserializeUser(row) {
  if (!row) {
    return null;
  }
  
  return {
    id: row.id,
    email: row.email,
    name: row.name,
    passwordHash: row.password_hash,
    providers: JSON.parse(row.providers || '[]'),
    emailVerified: Boolean(row.email_verified),
    verificationToken: row.verification_token,
    verificationExpiresAt: row.verification_expires_at,
    resetToken: row.reset_token,
    resetExpiresAt: row.reset_expires_at,
    plan: row.plan,
    billingStatus: row.billing_status,
    billingEmail: row.billing_email,
    paymentMethodLast4: row.payment_method_last4,
    subscriptionRenewsAt: row.subscription_renews_at,
    referredByAffiliateCode: row.referred_by_affiliate_code,
    affiliateAttributionAt: row.affiliate_attribution_at,
    affiliatePayouts: JSON.parse(row.affiliate_payouts || '[]'),
    twoFactorSecret: row.two_factor_secret ? decrypt(row.two_factor_secret) : null,
    twoFactorEnabled: Boolean(row.two_factor_enabled),
    createdAt: row.created_at,
    updatedAt: row.updated_at,
    lastLoginAt: row.last_login_at
  };
}

module.exports = {
  createUser,
  getUserById,
  getUserByEmail,
  getUserByVerificationToken,
  getUserByResetToken,
  updateUser,
  deleteUser,
  getAllUsers,
  countUsers
};
