const bcrypt = require("bcryptjs");
const couchdbService = require("../services/couchdbService");
const {
  ValidationError,
  withErrorHandling,
  createErrorContext,
} = require("../utils/modelErrors");

const URL_REGEX = /^(https?|ftp):\/\/[^\s\/$.?#].[^\s]*$/i;

class User {
  constructor(data) { // Validate required fields for new user creation
    if (!data._id) { // Only for new users
      if (!data.name) { throw new ValidationError("Name is required", "name", data.name); }
      if (!data.email) { throw new ValidationError("Email is required", "email", data.email); }
      if (!data.password) { throw new ValidationError("Password is required", "password", data.password); }
    }

    this._id = data._id || null;
    this._rev = data._rev || null;
    this.type = "user";
    this.name = data.name;
    this.email = data.email;
    this.password = data.password;

    // --- Profile Information ---
    this.avatar = data.avatar || null;
    this.profilePicture = data.profilePicture || data.avatar || null;
    this.cloudinaryPublicId = data.cloudinaryPublicId || null;
    this.bio = data.bio || "";
    if (this.bio.length > 500) { throw new ValidationError("Bio cannot exceed 500 characters.", "bio", this.bio); }
    this.location = data.location || "";
    this.website = data.website || "";
    if (this.website && !URL_REGEX.test(this.website)) { throw new ValidationError("Invalid website URL.", "website", this.website); }

    // --- Social Links ---
    this.social = data.social || { twitter: "", github: "", linkedin: "" };
    if (this.social.twitter && !URL_REGEX.test(this.social.twitter)) { throw new ValidationError("Invalid Twitter URL.", "social.twitter", this.social.twitter); }
    if (this.social.github && !URL_REGEX.test(this.social.github)) { throw new ValidationError("Invalid Github URL.", "social.github", this.social.github); }
    if (this.social.linkedin && !URL_REGEX.test(this.social.linkedin)) { throw new ValidationError("Invalid LinkedIn URL.", "social.linkedin", this.social.linkedin); }

    // --- Settings & Preferences ---
    this.privacySettings = data.privacySettings || { profileVisibility: "public" };
    if (!["public", "private"].includes(this.privacySettings.profileVisibility)) { throw new ValidationError("Profile visibility must be 'public' or 'private'.", "privacySettings.profileVisibility", this.privacySettings.profileVisibility); }
    this.preferences = data.preferences || { emailNotifications: true, pushNotifications: true, theme: "light" };
     if (!["light", "dark"].includes(this.preferences.theme)) { throw new ValidationError("Theme must be 'light' or 'dark'.", "preferences.theme", this.preferences.theme); }


    // --- Gamification & App Data ---
    this.isPremium = data.isPremium || false;
    this.points = Math.max(0, data.points || 0);
    this.adoptedStreets = data.adoptedStreets || [];
    this.completedTasks = data.completedTasks || [];
    this.posts = data.posts || [];
    this.events = data.events || [];
    this.earnedBadges = data.earnedBadges || [];
    this.stats = data.stats || {
      streetsAdopted: 0,
      tasksCompleted: 0,
      postsCreated: 0,
      eventsParticipated: 0,
      badgesEarned: 0,
    };

    this.createdAt = data.createdAt || new Date().toISOString();
    this.updatedAt = data.updatedAt || new Date().toISOString();
  }

  // ... (static methods remain the same)
  // Static methods for MongoDB compatibility
  static async findOne(query) {
    const errorContext = createErrorContext('User', 'findOne', { query });
    
    return await withErrorHandling(async () => {
      let user;
      if (query.email) { user = await couchdbService.findUserByEmail(query.email); }
      else if (query._id) { user = await couchdbService.findUserById(query._id); }
      else { // Generic query fallback
        const docs = await couchdbService.find({
          selector: { type: "user", ...query },
          limit: 1
        });
        user = docs[0] || null;
      }
      return user ? new User(user) : null;
    }, errorContext);
  }

  static async findById(id) {
    const errorContext = createErrorContext('User', 'findById', { id });
    
    return await withErrorHandling(async () => {
      const user = await couchdbService.findUserById(id);
      return user ? new User(user) : null;
    }, errorContext);
  }

  static async findByIdAndUpdate(id, update, options = {}) {
    const errorContext = createErrorContext('User', 'findByIdAndUpdate', { id, update, options });
    
    return await withErrorHandling(async () => {
      const user = await couchdbService.findUserById(id);
      if (!user) return null;

      const updatedUser = { ...user, ...update, updatedAt: new Date().toISOString() };
      const saved = await couchdbService.update(id, updatedUser);
      
      if (options.new) { return new User(saved); }
      return new User(user);
    }, errorContext);
  }

  static async findByIdAndDelete(id) {
    const errorContext = createErrorContext('User', 'findByIdAndDelete', { id });
    
    return await withErrorHandling(async () => {
      const user = await couchdbService.findUserById(id);
      if (!user) return null;
      
      await couchdbService.delete(id);
      return user;
    }, errorContext);
  }

  static async find(query = {}) {
    const errorContext = createErrorContext('User', 'find', { query });
    
    return await withErrorHandling(async () => {
      const selector = { type: "user", ...query };
      const users = await couchdbService.find({ selector });
      return users.map(u => new User(u));
    }, errorContext);
  }

  static async create(userData) {
    const errorContext = createErrorContext('User', 'create', { userData: { ...userData, password: '[REDACTED]' } });
    
    return await withErrorHandling(async () => {
      const user = new User(userData);
      
      if (user.password) { const salt = await bcrypt.genSalt(10); user.password = await bcrypt.hash(user.password, salt); }

      if (!user._id) { user._id = `user_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; }

      const created = await couchdbService.createDocument(user.toJSON());
      return new User(created);
    }, errorContext);
  }

  async save() {
    const errorContext = createErrorContext('User', 'save', { 
      id: this._id, 
      email: this.email,
      isNew: !this._id 
    });
    
    return await withErrorHandling(async () => {
      this.updatedAt = new Date().toISOString();
      if (!this._id) {
        this._id = `user_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
        if (this.password && !this.password.startsWith('$2')) {
          const salt = await bcrypt.genSalt(10);
          this.password = await bcrypt.hash(this.password, salt);
        }
        const created = await couchdbService.createDocument(this.toJSON());
        this._rev = created._rev;
        return this;
      } else {
        const updated = await couchdbService.updateDocument(this.toJSON());
        this._rev = updated._rev;
        return this;
      }
    }, errorContext);
  }

  async comparePassword(candidatePassword) {
    const errorContext = createErrorContext('User', 'comparePassword', { 
      id: this._id, 
      email: this.email 
    });
    
    return await withErrorHandling(async () => {
      return await bcrypt.compare(candidatePassword, this.password);
    }, errorContext);
  }

  toSafeObject() {
    const obj = this.toJSON();
    delete obj.password;
    return obj;
  }

  toJSON() {
    return {
      _id: this._id,
      _rev: this._rev,
      type: this.type,
      name: this.name,
      email: this.email,
      password: this.password,
      avatar: this.avatar,
      profilePicture: this.profilePicture,
      cloudinaryPublicId: this.cloudinaryPublicId,
      bio: this.bio,
      location: this.location,
      website: this.website,
      social: this.social,
      privacySettings: this.privacySettings,
      preferences: this.preferences,
      isPremium: this.isPremium,
      points: this.points,
      adoptedStreets: this.adoptedStreets,
      completedTasks: this.completedTasks,
      posts: this.posts,
      events: this.events,
      earnedBadges: this.earnedBadges,
      stats: this.stats,
      createdAt: this.createdAt,
      updatedAt: this.updatedAt,
    };
  }
}

module.exports = User;
