const bcrypt = require('bcryptjs');
const { v4: uuidv4 } = require('uuid');
const database = require('../config/database');

class User {
  constructor(data = {}) {
    this.id = data.id;
    this.username = data.username;
    this.email = data.email;
    this.password_hash = data.password_hash;
    this.first_name = data.first_name;
    this.last_name = data.last_name;
    this.role = data.role;
    this.email_verified = data.email_verified;
    this.is_active = data.is_active;
    this.last_login = data.last_login;
    this.created_at = data.created_at;
    this.updated_at = data.updated_at;
  }

  // Create new user
  static async create(userData) {
    const { username, email, password, first_name, last_name, role = 'user' } = userData;
    
    // Hash password
    const saltRounds = 12;
    const password_hash = await bcrypt.hash(password, saltRounds);
    
    const query = `
      INSERT INTO users (
        id, username, email, password_hash, first_name, last_name, role
      ) VALUES ($1, $2, $3, $4, $5, $6, $7)
      RETURNING id, username, email, first_name, last_name, role, email_verified, is_active, created_at
    `;
    
    const values = [
      uuidv4(),
      username.toLowerCase(),
      email.toLowerCase(),
      password_hash,
      first_name,
      last_name,
      role
    ];
    
    try {
      const result = await database.query(query, values);
      return new User(result.rows[0]);
    } catch (error) {
      if (error.code === '23505') { // Unique constraint violation
        if (error.constraint.includes('email')) {
          throw new Error('Email already exists');
        }
        if (error.constraint.includes('username')) {
          throw new Error('Username already exists');
        }
      }
      throw error;
    }
  }

  // Find user by email
  static async findByEmail(email) {
    const query = `
      SELECT * FROM users 
      WHERE email = $1 AND is_active = true
    `;
    
    const result = await database.query(query, [email.toLowerCase()]);
    return result.rows.length > 0 ? new User(result.rows[0]) : null;
  }

  // Find user by username
  static async findByUsername(username) {
    const query = `
      SELECT * FROM users 
      WHERE username = $1 AND is_active = true
    `;
    
    const result = await database.query(query, [username.toLowerCase()]);
    return result.rows.length > 0 ? new User(result.rows[0]) : null;
  }

  // Find user by ID
  static async findById(userId) {
    const query = `
      SELECT * FROM users 
      WHERE id = $1 AND is_active = true
    `;
    
    const result = await database.query(query, [userId]);
    return result.rows.length > 0 ? new User(result.rows[0]) : null;
  }

  // Verify password
  async verifyPassword(password) {
    return await bcrypt.compare(password, this.password_hash);
  }

  // Update last login
  async updateLastLogin() {
    const query = `
      UPDATE users 
      SET last_login = NOW() 
      WHERE id = $1
      RETURNING last_login
    `;
    
    const result = await database.query(query, [this.id]);
    this.last_login = result.rows[0].last_login;
    return this.last_login;
  }

  // Update user profile
  async updateProfile(updates) {
    const allowedFields = ['first_name', 'last_name', 'username'];
    const setClause = [];
    const values = [];
    let paramCount = 1;

    for (const [field, value] of Object.entries(updates)) {
      if (allowedFields.includes(field) && value !== undefined) {
        setClause.push(`${field} = $${paramCount}`);
        values.push(field === 'username' ? value.toLowerCase() : value);
        paramCount++;
      }
    }

    if (setClause.length === 0) {
      throw new Error('No valid fields to update');
    }

    values.push(this.id);
    const query = `
      UPDATE users 
      SET ${setClause.join(', ')}, updated_at = NOW()
      WHERE id = $${paramCount}
      RETURNING id, username, email, first_name, last_name, role, updated_at
    `;

    try {
      const result = await database.query(query, values);
      Object.assign(this, result.rows[0]);
      return this;
    } catch (error) {
      if (error.code === '23505' && error.constraint.includes('username')) {
        throw new Error('Username already exists');
      }
      throw error;
    }
  }

  // Change password
  async changePassword(currentPassword, newPassword) {
    // Verify current password
    const isCurrentValid = await this.verifyPassword(currentPassword);
    if (!isCurrentValid) {
      throw new Error('Current password is incorrect');
    }

    // Hash new password
    const saltRounds = 12;
    const newPasswordHash = await bcrypt.hash(newPassword, saltRounds);

    const query = `
      UPDATE users 
      SET password_hash = $1, updated_at = NOW()
      WHERE id = $2
    `;

    await database.query(query, [newPasswordHash, this.id]);
    this.password_hash = newPasswordHash;
    
    return true;
  }

  // Get user's feature preferences for a template
  async getFeaturePreferences(templateType) {
    const query = `
      SELECT feature_id, preference_type, custom_data, updated_at
      FROM user_feature_preferences 
      WHERE user_id = $1 AND template_type = $2
      ORDER BY updated_at DESC
    `;
    
    const result = await database.query(query, [this.id, templateType]);
    return result.rows;
  }

  // Set feature preference (remove/add/customize)
  async setFeaturePreference(templateType, featureId, preferenceType, customData = null) {
    const query = `
      INSERT INTO user_feature_preferences (
        user_id, template_type, feature_id, preference_type, custom_data
      ) VALUES ($1, $2, $3, $4, $5)
      ON CONFLICT (user_id, template_type, feature_id, preference_type) 
      DO UPDATE SET 
        custom_data = EXCLUDED.custom_data,
        updated_at = NOW()
      RETURNING *
    `;
    
    const values = [this.id, templateType, featureId, preferenceType, customData];
    const result = await database.query(query, values);
    return result.rows[0];
  }

  // Remove feature preference
  async removeFeaturePreference(templateType, featureId, preferenceType) {
    const query = `
      DELETE FROM user_feature_preferences 
      WHERE user_id = $1 AND template_type = $2 AND feature_id = $3 AND preference_type = $4
      RETURNING *
    `;
    
    const result = await database.query(query, [this.id, templateType, featureId, preferenceType]);
    return result.rows.length > 0;
  }

  // Get user's projects
  async getProjects(limit = 10) {
    const query = `
      SELECT * FROM user_projects 
      WHERE user_id = $1 AND is_active = true
      ORDER BY updated_at DESC
      LIMIT $2
    `;
    
    const result = await database.query(query, [this.id, limit]);
    return result.rows;
  }

  // Save user project
  async saveProject(projectData) {
    const { project_name, project_type, selected_features, custom_features, project_data } = projectData;
    
    const query = `
      INSERT INTO user_projects (
        user_id, project_name, project_type, selected_features, custom_features, project_data
      ) VALUES ($1, $2, $3, $4, $5, $6)
      RETURNING *
    `;
    
    const values = [
      this.id,
      project_name,
      project_type,
      JSON.stringify(selected_features || []),
      JSON.stringify(custom_features || []),
      JSON.stringify(project_data || {})
    ];
    
    const result = await database.query(query, values);
    return result.rows[0];
  }

  // Get user statistics
  async getStats() {
    const query = `
      SELECT 
        (SELECT COUNT(*) FROM user_projects WHERE user_id = $1 AND is_active = true) as total_projects,
        (SELECT COUNT(*) FROM user_feature_preferences WHERE user_id = $1) as feature_preferences,
        (SELECT COUNT(*) FROM user_sessions WHERE user_id = $1 AND is_active = true) as active_sessions,
        (SELECT last_login FROM users WHERE id = $1) as last_login,
        (SELECT created_at FROM users WHERE id = $1) as member_since
    `;
    
    const result = await database.query(query, [this.id]);
    return result.rows[0];
  }

  // Convert to safe JSON (remove sensitive data)
  toJSON() {
    const { password_hash, ...safeUser } = this;
    return safeUser;
  }

  // Get public profile
  getPublicProfile() {
    return {
      id: this.id,
      username: this.username,
      first_name: this.first_name,
      last_name: this.last_name,
      role: this.role,
      created_at: this.created_at
    };
  }

  // Static method to get user statistics
  static async getUserStats() {
    const query = `
      SELECT 
        COUNT(*) as total_users,
        COUNT(*) FILTER (WHERE is_active = true) as active_users,
        COUNT(*) FILTER (WHERE created_at > NOW() - INTERVAL '30 days') as new_users_30d,
        COUNT(*) FILTER (WHERE last_login > NOW() - INTERVAL '7 days') as active_users_7d
      FROM users
    `;
    
    const result = await database.query(query);
    return result.rows[0];
  }

  // Validate email format
  static validateEmail(email) {
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return emailRegex.test(email);
  }

  // Validate password strength
  static validatePassword(password) {
    if (password.length < 8) {
      return { valid: false, message: 'Password must be at least 8 characters long' };
    }
    if (!/(?=.*[a-z])/.test(password)) {
      return { valid: false, message: 'Password must contain at least one lowercase letter' };
    }
    if (!/(?=.*[A-Z])/.test(password)) {
      return { valid: false, message: 'Password must contain at least one uppercase letter' };
    }
    if (!/(?=.*\d)/.test(password)) {
      return { valid: false, message: 'Password must contain at least one number' };
    }
    return { valid: true, message: 'Password is valid' };
  }
}

module.exports = User;