const BaseModel = require('./BaseModel');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const mongoose = require('mongoose');

const userSchema = new mongoose.Schema({
  name: {
    type: String,
    required: [true, 'Please enter your name'],
    trim: true,
    maxlength: [50, 'Name cannot exceed 50 characters']
  },
  email: {
    type: String,
    required: [true, 'Please enter your email'],
    unique: true,
    lowercase: true,
    match: [/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/, 'Please enter a valid email']
  },
  phone: {
    type: String,
    required: [true, 'Please enter your phone number'],
    unique: true
  },
  password: {
    type: String,
    required: [true, 'Please enter your password'],
    minlength: [6, 'Password must be at least 6 characters'],
    select: false
  },
  role: {
    type: String,
    enum: ['superadmin', 'admin', 'store_manager', 'delivery_boy', 'customer', 'pharmacist', 'picker_packer'],
    default: 'customer'
  },
  status: {
    type: Boolean,
    default: true
  },
  avatar: {
    public_id: {
      type: String,
      default: ''
    },
    url: {
      type: String,
      default: ''
    }
  },
  resetPasswordToken: String,
  store: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Store',
    default: null
  },
  storeManager: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Store',
    default: null
  },
  // Location fields
  location: {
    address: {
      type: String,
      trim: true
    },
    city: {
      type: String,
      trim: true
    },
    state: {
      type: String,
      trim: true
    },
    country: {
      type: String,
      trim: true,
      default: 'India'
    },
    coordinates: {
      // GeoJSON Point
      type: {
        type: String,
        enum: ['Point'],
        default: 'Point'
      },
      coordinates: {
        type: [Number], // [longitude, latitude]
        default: [0, 0]
      }
    },
    formattedAddress: String,
    pincode: String,
    isDefault: {
      type: Boolean,
      default: true
    },
    lastUpdated: {
      type: Date,
      default: Date.now
    }
  },
  // Create 2dsphere index for geospatial queries
  resetPasswordExpire: Date,
  emailVerified: {
    type: Boolean,
    default: false
  },
  phoneVerified: {
    type: Boolean,
    default: false
  },
  addresses: [{
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Address'
  }],
  defaultAddress: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Address'
  },
  deliveryBoy: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'DeliveryBoy',
    default: null
  },
  storeManager: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Store',
    default: null
  },
  pharmacist: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Pharmacist',
    default: null
  },
  pickerPacker: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'PickerPacker',
    default: null
  },
  lastLogin: Date,
  loginAttempts: {
    type: Number,
    default: 0
  },
  lockUntil: Date,
  deletedAt: Date
}, {
  timestamps: true,
  toJSON: { virtuals: true },
  toObject: { virtuals: true }
});

// Encrypt password before saving
userSchema.pre('save', async function(next) {
  try {
    // Only hash the password if it has been modified (or is new)
    if (!this.isModified('password')) return next();
    
    if (!this.password) {
      console.error('No password provided for hashing');
      return next(new Error('Password is required'));
    }
    
    console.log('Hashing password for user:', this.email);
    const salt = await bcrypt.genSalt(10);
    this.password = await bcrypt.hash(this.password, salt);
    next();
  } catch (error) {
    console.error('Error hashing password:', error);
    next(error);
  }
});

// Sign JWT and return
userSchema.methods.getSignedJwtToken = function() {
  return jwt.sign(
    { id: this._id, role: this.role },
    process.env.JWT_SECRET,
    { expiresIn: process.env.JWT_EXPIRE || '30d' }
  );
};

// Match user entered password to hashed password in database
userSchema.methods.matchPassword = async function(enteredPassword) {
  try {
    if (!enteredPassword) {
      console.error('No password provided for comparison');
      return false;
    }
    if (!this.password) {
      console.error('No hashed password found for user');
      return false;
    }
    
    // Add logging to debug the password comparison
    console.log('Comparing passwords...');
    console.log('Entered password length:', enteredPassword.length);
    console.log('Stored password hash:', this.password ? 'exists' : 'missing');
    
    if (typeof enteredPassword !== 'string') {
      console.error('Entered password is not a string');
      return false;
    }
    
    if (typeof this.password !== 'string') {
      console.error('Stored password is not a string');
      return false;
    }
    
    const isMatch = await bcrypt.compare(enteredPassword, this.password);
    console.log('Password match result:', isMatch);
    
    return isMatch;
  } catch (error) {
    console.error('Error in matchPassword:', error);
    throw error; // Re-throw the error to be caught by the login controller
  }
};

// Virtual for role redirect path
userSchema.virtual('redirectPath').get(function() {
  const roleMappings = {
    superadmin: '/admin/dashboard',
    store_manager: '/store/dashboard',
    delivery_boy: '/delivery/dashboard',
    customer: '/'
  };
  return roleMappings[this.role] || '/';
});

// Create the User model
const User = mongoose.model('User', userSchema);

// Create indexes
User.collection.createIndex({ email: 1 }, { unique: true });
User.collection.createIndex({ phone: 1 }, { unique: true });

// Add static methods
userSchema.statics.findByEmail = async function(email) {
  return this.findOne({ email });
};

// Update user location
userSchema.statics.updateLocation = async function(userId, locationData) {
  const {
    address,
    city,
    state,
    country,
    coordinates,
    formattedAddress,
    pincode
  } = locationData;

  return this.findByIdAndUpdate(
    userId,
    {
      $set: {
        'location.address': address,
        'location.city': city,
        'location.state': state,
        'location.country': country,
        'location.coordinates': {
          type: 'Point',
          coordinates: coordinates || [0, 0]
        },
        'location.formattedAddress': formattedAddress,
        'location.pincode': pincode,
        'location.lastUpdated': Date.now()
      }
    },
    { new: true, runValidators: true }
  ).select('-password');
};

User.getProfile = async function(userId) {
  const user = await this.findById(userId)
    .select('-password -resetPasswordToken -resetPasswordExpire')
    .populate({
      path: 'defaultAddress',
      select: 'country state city postcode address location'
    });
    
  if (!user) return null;
  
  // Convert to plain object to handle virtuals
  const userObj = user.toObject();
  
  // Add role permissions
  userObj.permissions = User.getRolePermissions(user.role);
  
  return userObj;
};

User.getRolePermissions = function(role) {
  // Define permissions for each role
  const rolePermissions = {
    superadmin: ['*'],
    admin: ['*'], // Admin has all permissions
    store_manager: ['manage_products', 'manage_orders', 'view_reports'],
    delivery_boy: ['update_order_status', 'view_assigned_orders'],
    customer: ['place_orders', 'view_own_orders', 'manage_own_profile']
  };
  
  return rolePermissions[role] || [];
};

// Create 2dsphere index for location queries
userSchema.index({ 'location.coordinates': '2dsphere' });

module.exports = mongoose.model('User', userSchema);
