const mongoose = require("mongoose");

// Individual time slot schema
const timeSlotSchema = new mongoose.Schema({
  startTime: {
    type: String,
    required: true,
    match: /^([01]\d|2[0-3]):([0-5]\d)$/,
  },
  endTime: {
    type: String,
    required: true,
    match: /^([01]\d|2[0-3]):([0-5]\d)$/,
  },
  isRecurring: {
    type: Boolean,
    default: true
  },
  // For one-time slots, specify the exact date
  specificDate: {
    type: Date,
    default: null
  },
  // Status of the slot
  status: {
    type: String,
    enum: ["live", "pending_approval", "unavailable", "conflict"],
    default: "pending_approval"
  },
  // Admin approval tracking
  approvalStatus: {
    type: String,
    enum: ["pending", "approved", "rejected"],
    default: "pending"
  },
  approvedBy: {
    type: mongoose.Schema.Types.ObjectId,
    ref: "User",
    default: null
  },
  approvedAt: {
    type: Date,
    default: null
  },
  rejectionReason: {
    type: String,
    default: null
  }
}, { _id: true });

// Daily availability schema
const dailyAvailabilitySchema = new mongoose.Schema({
  dayOfWeek: {
    type: String,
    required: true,
    enum: ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"]
  },
  isAvailable: {
    type: Boolean,
    default: true
  },
  slots: [timeSlotSchema],
  // Mark entire day as unavailable
  isUnavailable: {
    type: Boolean,
    default: false
  },
  unavailableReason: {
    type: String,
    default: null
  }
}, { _id: false });

// Main therapist availability schema
const therapistAvailabilitySchema = new mongoose.Schema({
  therapist: {
    type: mongoose.Schema.Types.ObjectId,
    ref: "User",
    required: true,
    unique: true,
    index: true
  },
  
  // Weekly recurring availability
  weeklyAvailability: [dailyAvailabilitySchema],
  
  // One-time availability changes (overrides weekly for specific dates)
  oneTimeSlots: [timeSlotSchema],
  
  // Unavailable dates (vacations, leaves, etc.)
  unavailableDates: [{
    date: {
      type: Date,
      required: true
    },
    reason: {
      type: String,
      default: "Unavailable"
    },
    isFullDay: {
      type: Boolean,
      default: true
    },
    // If not full day, specify unavailable time ranges
    unavailableSlots: [{
      startTime: String,
      endTime: String
    }],
    approvalStatus: {
      type: String,
      enum: ["pending", "approved", "rejected"],
      default: "pending"
    },
    approvedBy: {
      type: mongoose.Schema.Types.ObjectId,
      ref: "User",
      default: null
    }
  }],
  
  // Settings and preferences
  settings: {
    // Maximum sessions per day
    maxSessionsPerDay: {
      type: Number,
      default: 7,
      min: 1,
      max: 12
    },
    
    // Default session duration (in minutes)
    defaultSessionDuration: {
      type: Number,
      default: 60,
      enum: [30, 45, 60, 90, 120]
    },
    
    // Break between sessions (in minutes)
    breakBetweenSessions: {
      type: Number,
      default: 30,
      min: 0,
      max: 60
    },
    
    // Auto-approve minor changes (time shifts < 30 minutes)
    autoApproveMinorChanges: {
      type: Boolean,
      default: false
    },
    
    // Advance booking limit (how many days in advance clients can book)
    advanceBookingDays: {
      type: Number,
      default: 30,
      min: 1,
      max: 90
    },
    
    // Time zone
    timeZone: {
      type: String,
      default: "Asia/Kolkata"
    }
  },
  
  // Pending changes awaiting admin approval
  pendingChanges: [{
    changeType: {
      type: String,
      enum: ["add_slots", "remove_slots", "modify_slots", "add_unavailable_date", "modify_settings"],
      required: true
    },
    changeData: {
      type: mongoose.Schema.Types.Mixed,
      required: true
    },
    requestedAt: {
      type: Date,
      default: Date.now
    },
    reason: {
      type: String,
      default: null
    },
    status: {
      type: String,
      enum: ["pending", "approved", "rejected"],
      default: "pending"
    },
    adminResponse: {
      approvedBy: {
        type: mongoose.Schema.Types.ObjectId,
        ref: "User",
        default: null
      },
      responseAt: {
        type: Date,
        default: null
      },
      feedback: {
        type: String,
        default: null
      }
    }
  }],
  
  // Analytics and tracking
  analytics: {
    totalSlotsCreated: {
      type: Number,
      default: 0
    },
    totalSlotsBooked: {
      type: Number,
      default: 0
    },
    averageBookingRate: {
      type: Number,
      default: 0
    },
    peakHours: [{
      hour: Number,
      bookingCount: Number
    }],
    lastUpdated: {
      type: Date,
      default: Date.now
    }
  }
}, { 
  timestamps: true,
  toJSON: { virtuals: true },
  toObject: { virtuals: true }
});

// Indexes for better performance
therapistAvailabilitySchema.index({ therapist: 1 });
therapistAvailabilitySchema.index({ "weeklyAvailability.dayOfWeek": 1 });
therapistAvailabilitySchema.index({ "oneTimeSlots.specificDate": 1 });
therapistAvailabilitySchema.index({ "unavailableDates.date": 1 });
therapistAvailabilitySchema.index({ "pendingChanges.status": 1 });

// Virtual for getting current week availability
therapistAvailabilitySchema.virtual('currentWeekSlots').get(function() {
  const now = new Date();
  const startOfWeek = new Date(now.setDate(now.getDate() - now.getDay()));
  const endOfWeek = new Date(now.setDate(now.getDate() - now.getDay() + 6));
  
  // Combine weekly recurring slots with one-time slots for current week
  const weekSlots = [];
  
  // Add recurring weekly slots
  this.weeklyAvailability.forEach(day => {
    if (day.isAvailable && !day.isUnavailable) {
      day.slots.forEach(slot => {
        if (slot.status === 'live') {
          weekSlots.push({
            ...slot.toObject(),
            dayOfWeek: day.dayOfWeek,
            isRecurring: true
          });
        }
      });
    }
  });
  
  // Add one-time slots for current week
  this.oneTimeSlots.forEach(slot => {
    if (slot.specificDate >= startOfWeek && slot.specificDate <= endOfWeek && slot.status === 'live') {
      weekSlots.push({
        ...slot.toObject(),
        isRecurring: false
      });
    }
  });
  
  return weekSlots;
});

// Method to check if therapist is available at specific date/time
therapistAvailabilitySchema.methods.isAvailableAt = function(date, startTime, endTime) {
  const dayOfWeek = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'][date.getDay()];
  
  // Check if date is marked as unavailable
  const unavailableDate = this.unavailableDates.find(ud => 
    ud.date.toDateString() === date.toDateString() && 
    ud.approvalStatus === 'approved'
  );
  
  if (unavailableDate && unavailableDate.isFullDay) {
    return false;
  }
  
  // Check one-time slots first (they override weekly availability)
  const oneTimeSlot = this.oneTimeSlots.find(slot => 
    slot.specificDate && 
    slot.specificDate.toDateString() === date.toDateString() &&
    slot.startTime === startTime &&
    slot.endTime === endTime &&
    slot.status === 'live'
  );
  
  if (oneTimeSlot) {
    return true;
  }
  
  // Check weekly recurring availability
  const dayAvailability = this.weeklyAvailability.find(day => day.dayOfWeek === dayOfWeek);
  
  if (!dayAvailability || !dayAvailability.isAvailable || dayAvailability.isUnavailable) {
    return false;
  }
  
  // Check if specific time slot exists and is live
  const slot = dayAvailability.slots.find(s => 
    s.startTime === startTime && 
    s.endTime === endTime && 
    s.status === 'live'
  );
  
  return !!slot;
};

// Method to get available slots for a specific date
therapistAvailabilitySchema.methods.getAvailableSlotsForDate = function(date) {
  const dayOfWeek = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'][date.getDay()];
  
  // Check if date is marked as unavailable
  const unavailableDate = this.unavailableDates.find(ud => 
    ud.date.toDateString() === date.toDateString() && 
    ud.approvalStatus === 'approved'
  );
  
  if (unavailableDate && unavailableDate.isFullDay) {
    return [];
  }
  
  let availableSlots = [];
  
  // Get one-time slots for this date
  const oneTimeSlots = this.oneTimeSlots.filter(slot => 
    slot.specificDate && 
    slot.specificDate.toDateString() === date.toDateString() &&
    slot.status === 'live'
  );
  
  availableSlots = [...oneTimeSlots];
  
  // If no one-time slots, get weekly recurring slots
  if (availableSlots.length === 0) {
    const dayAvailability = this.weeklyAvailability.find(day => day.dayOfWeek === dayOfWeek);
    
    if (dayAvailability && dayAvailability.isAvailable && !dayAvailability.isUnavailable) {
      const liveSlots = dayAvailability.slots.filter(slot => slot.status === 'live');
      availableSlots = [...liveSlots];
    }
  }
  
  // Filter out unavailable time slots if partial day unavailability
  if (unavailableDate && !unavailableDate.isFullDay && unavailableDate.unavailableSlots) {
    availableSlots = availableSlots.filter(slot => {
      return !unavailableDate.unavailableSlots.some(unavailableSlot => 
        slot.startTime === unavailableSlot.startTime && 
        slot.endTime === unavailableSlot.endTime
      );
    });
  }
  
  return availableSlots;
};

// Static method to initialize default availability for new therapist
therapistAvailabilitySchema.statics.createDefaultAvailability = async function(therapistId) {
  const defaultWeeklyAvailability = [
    { dayOfWeek: 'monday', isAvailable: true, slots: [] },
    { dayOfWeek: 'tuesday', isAvailable: true, slots: [] },
    { dayOfWeek: 'wednesday', isAvailable: true, slots: [] },
    { dayOfWeek: 'thursday', isAvailable: true, slots: [] },
    { dayOfWeek: 'friday', isAvailable: true, slots: [] },
    { dayOfWeek: 'saturday', isAvailable: false, slots: [] },
    { dayOfWeek: 'sunday', isAvailable: false, slots: [] }
  ];
  
  const availability = new this({
    therapist: therapistId,
    weeklyAvailability: defaultWeeklyAvailability
  });
  
  return await availability.save();
};

module.exports = mongoose.model("TherapistAvailability", therapistAvailabilitySchema);
