const TherapistAvailability = require("../../models/TherapistAvailability");
const AvailabilityNotification = require("../../models/AvailabilityNotification");
const Appointment = require("../../models/Appointment");
const User = require("../../models/userModel");
const DoctorTimeSlot = require("../../models/TimeSlot"); // Keep for backward compatibility
const mongoose = require("mongoose");

// Enhanced function to get available slots using new availability system
const getEnhancedAvailableSlots = async (req, res) => {
  try {
    const { date, founder, therapistId } = req.query;

    if (!date || !date.match(/^\d{4}-\d{2}-\d{2}$/)) {
      return res.status(400).json({
        message: "Valid date is required in 'YYYY-MM-DD' format.",
      });
    }

    const queryDate = new Date(date);
    if (isNaN(queryDate.getTime())) {
      return res.status(400).json({ message: "Invalid date format." });
    }

    // Check if the query date is today
    const now = new Date();
    const isToday = queryDate.toDateString() === now.toDateString();
    const currentTime = isToday ? now.toTimeString().slice(0, 5) : null;

    let availabilityFilter = {};
    
    // Handle founder mode or specific therapist
    if (founder === "true" && process.env.FOUNDER_DOCTOR_ID) {
      availabilityFilter.therapist = process.env.FOUNDER_DOCTOR_ID;
    } else if (therapistId) {
      availabilityFilter.therapist = therapistId;
    }

    // Get therapist availabilities and existing appointments
    const [availabilities, appointments] = await Promise.all([
      TherapistAvailability.find(availabilityFilter)
        .populate('therapist', 'fullname email')
        .lean(),

      Appointment.find({
        date: queryDate,
        status: { $in: ["scheduled", "completed"] },
        ...(founder === "true" && process.env.FOUNDER_DOCTOR_ID && { doctor: process.env.FOUNDER_DOCTOR_ID }),
        ...(therapistId && { doctor: therapistId })
      })
        .select("doctor timeSlot")
        .lean(),
    ]);

    // Create a map of booked slots
    const bookedSlotMap = new Map();
    appointments.forEach((app) => {
      const key = `${app.doctor.toString()}_${app.timeSlot.startTime}-${app.timeSlot.endTime}`;
      bookedSlotMap.set(key, true);
    });

    const slotMap = new Map();

    // Process each therapist's availability
    availabilities.forEach((availability) => {
      if (!availability.therapist) return;

      // Get available slots for the specific date
      const availableSlots = availability.getAvailableSlotsForDate ? 
        availability.getAvailableSlotsForDate(queryDate) : 
        getAvailableSlotsForDateFallback(availability, queryDate);

      availableSlots.forEach((slot) => {
        // Skip slots that are not live
        if (slot.status !== 'live') return;

        // Skip past slots if the date is today
        if (isToday && slot.startTime < currentTime) return;

        const slotKey = `${slot.startTime}-${slot.endTime}`;
        const bookingKey = `${availability.therapist._id.toString()}_${slotKey}`;

        // Skip booked slots
        if (bookedSlotMap.has(bookingKey)) return;

        if (!slotMap.has(slotKey)) {
          slotMap.set(slotKey, {
            startTime: slot.startTime,
            endTime: slot.endTime,
            doctors: [],
          });
        }

        slotMap.get(slotKey).doctors.push({
          doctorId: availability.therapist._id,
          name: availability.therapist.fullname,
          email: availability.therapist.email,
        });
      });
    });

    // Fallback to old system if no new availability data found
    if (availabilities.length === 0) {
      return await getAvailableSlotsLegacy(req, res);
    }

    const availableSlots = Array.from(slotMap.values());

    return res.status(200).json({
      date,
      founder: founder === "true",
      availableSlots,
      count: availableSlots.length,
      source: 'enhanced_availability_system'
    });

  } catch (error) {
    console.error("Error fetching enhanced available slots:", error);
    return res.status(500).json({
      message: "An error occurred while fetching available slots.",
      error: process.env.NODE_ENV === "production" ? "Server error" : error.message,
    });
  }
};

// Fallback function to get slots from availability data when method is not available
const getAvailableSlotsForDateFallback = (availability, date) => {
  const dayOfWeek = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'][date.getDay()];
  
  // Check if date is marked as unavailable
  const unavailableDate = availability.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 = availability.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 = availability.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;
};

// Legacy function for backward compatibility
const getAvailableSlotsLegacy = async (req, res) => {
  try {
    const { date, founder } = req.query;
    const queryDate = new Date(date);
    const now = new Date();
    const isToday = queryDate.toDateString() === now.toDateString();
    const currentTime = isToday ? now.toTimeString().slice(0, 5) : null;

    const isFounderMode = founder === "true";
    const doctorFilter = isFounderMode
      ? { doctor: process.env.FOUNDER_DOCTOR_ID }
      : {};

    const [doctorSlotsDocs, appointments] = await Promise.all([
      DoctorTimeSlot.find(doctorFilter)
        .select("doctor slots")
        .populate("doctor", "fullname email")
        .lean(),

      Appointment.find({
        date: queryDate,
        status: { $in: ["scheduled", "completed"] },
        ...(isFounderMode && { doctor: process.env.FOUNDER_DOCTOR_ID }),
      })
        .select("doctor timeSlot")
        .lean(),
    ]);

    const bookedSlotMap = new Map();
    appointments.forEach((app) => {
      const key = `${app.doctor.toString()}_${app.timeSlot.startTime}-${
        app.timeSlot.endTime
      }`;
      bookedSlotMap.set(key, true);
    });

    const slotMap = new Map();

    doctorSlotsDocs.forEach((doc) => {
      if (!doc.doctor) return;

      doc.slots.forEach((slot) => {
        const slotKey = `${slot.startTime}-${slot.endTime}`;
        const bookingKey = `${doc.doctor._id.toString()}_${slotKey}`;

        if (bookedSlotMap.has(bookingKey)) return;
        if (isToday && slot.startTime < currentTime) return;

        if (!slotMap.has(slotKey)) {
          slotMap.set(slotKey, {
            startTime: slot.startTime,
            endTime: slot.endTime,
            doctors: [],
          });
        }

        slotMap.get(slotKey).doctors.push({
          doctorId: doc.doctor._id,
          name: doc.doctor.fullname,
          email: doc.doctor.email,
        });
      });
    });

    const availableSlots = Array.from(slotMap.values());

    return res.status(200).json({
      date,
      founder: isFounderMode,
      availableSlots,
      count: availableSlots.length,
      source: 'legacy_system'
    });

  } catch (error) {
    console.error("Error fetching legacy available slots:", error);
    return res.status(500).json({
      message: "An error occurred while fetching available slots.",
      error: process.env.NODE_ENV === "production" ? "Server error" : error.message,
    });
  }
};

// Enhanced appointment booking with availability validation
const bookAppointmentWithAvailabilityCheck = async (req, res) => {
  try {
    const {
      date,
      timeSlot,
      doctorId,
      patientId,
      sessionType = "Consultation",
      mode = "Online",
      notes
    } = req.body;

    // Validate required fields
    if (!date || !timeSlot || !doctorId || !patientId) {
      return res.status(400).json({
        success: false,
        message: "Date, time slot, doctor ID, and patient ID are required"
      });
    }

    // Validate date format
    const appointmentDate = new Date(date);
    if (isNaN(appointmentDate.getTime())) {
      return res.status(400).json({
        success: false,
        message: "Invalid date format"
      });
    }

    // Check if appointment is in the past
    const now = new Date();
    if (appointmentDate < now) {
      return res.status(400).json({
        success: false,
        message: "Cannot book appointments in the past"
      });
    }

    // Validate time slot format
    if (!timeSlot.startTime || !timeSlot.endTime) {
      return res.status(400).json({
        success: false,
        message: "Time slot must have start and end times"
      });
    }

    // Check if therapist exists
    const therapist = await User.findById(doctorId);
    if (!therapist || therapist.role !== 'doctor') {
      return res.status(404).json({
        success: false,
        message: "Therapist not found"
      });
    }

    // Check if patient exists
    const patient = await User.findById(patientId);
    if (!patient) {
      return res.status(404).json({
        success: false,
        message: "Patient not found"
      });
    }

    // Check if slot is available using new availability system
    const availability = await TherapistAvailability.findOne({ therapist: doctorId });
    
    if (availability) {
      // Use new availability system
      const isAvailable = availability.isAvailableAt ? 
        availability.isAvailableAt(appointmentDate, timeSlot.startTime, timeSlot.endTime) :
        isAvailableAtFallback(availability, appointmentDate, timeSlot.startTime, timeSlot.endTime);

      if (!isAvailable) {
        return res.status(400).json({
          success: false,
          message: "Selected time slot is not available"
        });
      }

      // Check if slot is pending approval
      const dayOfWeek = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'][appointmentDate.getDay()];
      const dayAvailability = availability.weeklyAvailability?.find(day => day.dayOfWeek === dayOfWeek);
      
      if (dayAvailability) {
        const slot = dayAvailability.slots?.find(s => 
          s.startTime === timeSlot.startTime && 
          s.endTime === timeSlot.endTime
        );
        
        if (slot && slot.status === 'pending_approval') {
          // Create notification for admin about booking on pending slot
          const admins = await User.find({ role: 'admin' });
          for (const admin of admins) {
            await AvailabilityNotification.createAvailabilityChangeNotification({
              recipientId: admin._id,
              senderId: patientId,
              type: 'booking_on_pending_slot',
              availabilityId: availability._id,
              relatedDate: appointmentDate,
              timeSlots: [{ startTime: timeSlot.startTime, endTime: timeSlot.endTime }],
              metadata: {
                therapistId: doctorId,
                patientId: patientId,
                sessionType
              }
            });
          }
        }
      }
    } else {
      // Fallback to legacy system
      const doctorSlots = await DoctorTimeSlot.findOne({ doctor: doctorId });
      if (!doctorSlots) {
        return res.status(400).json({
          success: false,
          message: "Therapist has no available time slots"
        });
      }

      const slotExists = doctorSlots.slots.some(slot => 
        slot.startTime === timeSlot.startTime && 
        slot.endTime === timeSlot.endTime
      );

      if (!slotExists) {
        return res.status(400).json({
          success: false,
          message: "Selected time slot is not in therapist's schedule"
        });
      }
    }

    // Check for existing appointment at the same time
    const existingAppointment = await Appointment.findOne({
      doctor: doctorId,
      date: appointmentDate,
      'timeSlot.startTime': timeSlot.startTime,
      'timeSlot.endTime': timeSlot.endTime,
      status: { $in: ['scheduled', 'completed'] }
    });

    if (existingAppointment) {
      return res.status(400).json({
        success: false,
        message: "Time slot is already booked"
      });
    }

    // Create the appointment
    const appointment = new Appointment({
      date: appointmentDate,
      timeSlot: {
        startTime: timeSlot.startTime,
        endTime: timeSlot.endTime
      },
      doctor: doctorId,
      patient: patientId,
      sessionType,
      mode,
      notes: notes || null,
      status: 'scheduled'
    });

    await appointment.save();

    // Populate the appointment for response
    await appointment.populate([
      { path: 'doctor', select: 'fullname email' },
      { path: 'patient', select: 'fullname email' }
    ]);

    // Update analytics if using new availability system
    if (availability) {
      availability.analytics.totalSlotsBooked += 1;
      availability.analytics.lastUpdated = new Date();
      await availability.save();
    }

    return res.status(201).json({
      success: true,
      message: "Appointment booked successfully",
      data: appointment
    });

  } catch (error) {
    console.error("Book appointment error:", error);
    return res.status(500).json({
      success: false,
      message: "Server Error",
      error: error.message
    });
  }
};

// Fallback function for availability check
const isAvailableAtFallback = (availability, date, startTime, endTime) => {
  const dayOfWeek = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'][date.getDay()];
  
  // Check if date is marked as unavailable
  const unavailableDate = availability.unavailableDates?.find(ud => 
    ud.date.toDateString() === date.toDateString() && 
    ud.approvalStatus === 'approved'
  );
  
  if (unavailableDate && unavailableDate.isFullDay) {
    return false;
  }
  
  // Check one-time slots first
  const oneTimeSlot = availability.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 = availability.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;
};

// Get therapist's booked appointments for a date range
const getTherapistBookedSlots = async (req, res) => {
  try {
    const { therapistId, startDate, endDate } = req.query;

    if (!therapistId) {
      return res.status(400).json({
        success: false,
        message: "Therapist ID is required"
      });
    }

    const filter = { doctor: therapistId };

    if (startDate && endDate) {
      filter.date = {
        $gte: new Date(startDate),
        $lte: new Date(endDate)
      };
    }

    const appointments = await Appointment.find(filter)
      .populate('patient', 'fullname email')
      .select('date timeSlot status sessionType mode')
      .sort({ date: 1, 'timeSlot.startTime': 1 });

    return res.status(200).json({
      success: true,
      message: "Booked slots fetched successfully",
      data: appointments
    });

  } catch (error) {
    console.error("Get booked slots error:", error);
    return res.status(500).json({
      success: false,
      message: "Server Error",
      error: error.message
    });
  }
};

module.exports = {
  getEnhancedAvailableSlots,
  bookAppointmentWithAvailabilityCheck,
  getTherapistBookedSlots
};
