package com.example.cuida.ui.schedule;

import android.app.Application;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;

import com.example.cuida.data.model.Appointment;
import com.example.cuida.utils.AlarmScheduler;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.firestore.FirebaseFirestore;
import com.google.firebase.firestore.QueryDocumentSnapshot;
import com.google.firebase.firestore.ListenerRegistration;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

public class ScheduleViewModel extends AndroidViewModel {

    private final FirebaseFirestore db;
    private final FirebaseAuth auth;

    private final MutableLiveData<String> selectedDate = new MutableLiveData<>();
    private final MutableLiveData<String> selectedTime = new MutableLiveData<>();
    private final MutableLiveData<String> selectedDoctor = new MutableLiveData<>();
    private final MutableLiveData<List<TimeSlot>> timeSlots = new MutableLiveData<>();
    private final MutableLiveData<String> selectedDoctorSchedule = new MutableLiveData<>();
    private final MutableLiveData<Boolean> saveSuccess = new MutableLiveData<>();
    private final MutableLiveData<String> saveError = new MutableLiveData<>();
    private final MutableLiveData<List<String>> doctorsList = new MutableLiveData<>(new ArrayList<>());
    private final java.util.Map<String, String> doctorSchedules = new java.util.HashMap<>();
    
    private ListenerRegistration snapshotListener;

    public ScheduleViewModel(@NonNull Application application) {
        super(application);
        db = FirebaseFirestore.getInstance();
        auth = FirebaseAuth.getInstance();
        fetchDoctors();
    }

    private void fetchDoctors() {
        db.collection("medicos")
                .get()
                .addOnCompleteListener(task -> {
                    if (task.isSuccessful() && task.getResult() != null) {
                        List<String> docs = new ArrayList<>();
                        doctorSchedules.clear();
                        for (QueryDocumentSnapshot document : task.getResult()) {
                            String name = document.getString("nome");
                            if (name == null) name = document.getString("nome_completo");
                            if (name == null) name = document.getString("name");
                            
                            String specialty = document.getString("especialidade");
                            String gender = document.getString("genero");
                            String horario = document.getString("horario");
                            
                            if (name != null && !name.trim().isEmpty()) {
                                String displayName = name;
                                if (specialty != null && !specialty.trim().isEmpty()) {
                                    displayName += " - " + specialty;
                                }
                                if (!displayName.startsWith("Dr.") && !displayName.startsWith("Dra.")) {
                                    if ("Feminino".equalsIgnoreCase(gender)) {
                                        displayName = "Dra. " + displayName;
                                    } else {
                                        displayName = "Dr. " + displayName;
                                    }
                                }
                                docs.add(displayName);
                                if (horario != null) {
                                    doctorSchedules.put(displayName, horario);
                                }
                            }
                        }
                        doctorsList.postValue(docs);
                    } else {
                        Log.e("ScheduleViewModel", "Error getting doctors", task.getException());
                    }
                });
    }

    public void setDate(int year, int month, int dayOfMonth) {
        String date = String.format(java.util.Locale.US, "%02d/%02d/%04d", dayOfMonth, month + 1, year);
        selectedDate.setValue(date);
        loadTimeSlots(date);
    }

    public void setSelectedDoctor(String doctor) {
        selectedDoctor.setValue(doctor);
        String schedule = doctorSchedules.get(doctor);
        selectedDoctorSchedule.setValue(schedule != null ? "Horário: " + schedule : "Horário: 09:00 - 23:00");
        String date = selectedDate.getValue();
        if (date != null) {
            loadTimeSlots(date);
        }
    }

    public LiveData<String> getSelectedDoctorSchedule() {
        return selectedDoctorSchedule;
    }

    public LiveData<String> getSelectedDate() {
        return selectedDate;
    }

    public void setTime(String time) {
        selectedTime.setValue(time);
        List<TimeSlot> currentSlots = timeSlots.getValue();
        if (currentSlots != null) {
            for (TimeSlot slot : currentSlots) {
                slot.setSelected(slot.getTime().equals(time));
            }
            timeSlots.setValue(currentSlots);
        }
    }

    public LiveData<String> getSelectedTime() {
        return selectedTime;
    }

    public LiveData<List<TimeSlot>> getTimeSlots() {
        return timeSlots;
    }

    public LiveData<Boolean> getSaveSuccess() {
        return saveSuccess;
    }

    public LiveData<String> getSaveError() {
        return saveError;
    }

    public LiveData<List<String>> getDoctorsList() {
        return doctorsList;
    }

    private void loadTimeSlots(String date) {
        if (snapshotListener != null) {
            snapshotListener.remove();
            snapshotListener = null;
        }

        // Init slots immediately to prevent "disappearing" hours while waiting for network.
        timeSlots.setValue(generateTimeSlots(new ArrayList<>(), date));

        if (auth.getCurrentUser() == null) return;
        String doctor = selectedDoctor.getValue();

        if (doctor == null) {
            timeSlots.setValue(generateTimeSlots(new ArrayList<>(), date));
            return;
        }

        // Listen in REAL-TIME for appointments of the selected doctor on the selected date
        snapshotListener = db.collection("consultas")
                .whereEqualTo("date", date)
                .whereEqualTo("type", doctor)
                .addSnapshotListener((queryDocumentSnapshots, e) -> {
                    if (e != null) {
                        Log.e("ScheduleViewModel", "Listen failed.", e);
                        return;
                    }

                    List<String> bookedTimes = new ArrayList<>();
                    if (queryDocumentSnapshots != null) {
                        for (QueryDocumentSnapshot document : queryDocumentSnapshots) {
                            Appointment appt = document.toObject(Appointment.class);
                            if (appt.time != null && !bookedTimes.contains(appt.time)) {
                                bookedTimes.add(appt.time);
                            }
                        }
                    }

                    List<TimeSlot> slots = generateTimeSlots(bookedTimes, date);
                    timeSlots.setValue(slots);
                });
    }

    @Override
    protected void onCleared() {
        super.onCleared();
        if (snapshotListener != null) {
            snapshotListener.remove();
        }
    }

    private List<TimeSlot> generateTimeSlots(List<String> bookedTimes, String selectedDateStr) {
        List<TimeSlot> slots = new ArrayList<>();
        int startHour = 9;
        int endHour = 23;
        int startMinute = 0;
        int endMinute = 0;

        String doctor = selectedDoctor.getValue();
        String schedule = doctorSchedules.get(doctor);
        
        if (schedule != null && schedule.contains(" - ")) {
            try {
                String[] parts = schedule.split(" - ");
                String[] startParts = parts[0].split(":");
                String[] endParts = parts[1].split(":");
                startHour = Integer.parseInt(startParts[0]);
                startMinute = Integer.parseInt(startParts[1]);
                endHour = Integer.parseInt(endParts[0]);
                endMinute = Integer.parseInt(endParts[1]);
            } catch (Exception e) {
                Log.e("ScheduleViewModel", "Error parsing schedule: " + schedule);
            }
        }

        Calendar now = Calendar.getInstance();
        boolean isToday = false;

        if (selectedDateStr != null) {
            String todayStr = String.format(java.util.Locale.US, "%02d/%02d/%04d",
                    now.get(Calendar.DAY_OF_MONTH),
                    now.get(Calendar.MONTH) + 1,
                    now.get(Calendar.YEAR));
            if (todayStr.equals(selectedDateStr)) {
                isToday = true;
            }
        }

        int currentHour = now.get(Calendar.HOUR_OF_DAY);
        int currentMinute = now.get(Calendar.MINUTE);

        Calendar cursor = Calendar.getInstance();
        cursor.set(Calendar.HOUR_OF_DAY, startHour);
        cursor.set(Calendar.MINUTE, startMinute);
        cursor.set(Calendar.SECOND, 0);
        cursor.set(Calendar.MILLISECOND, 0);

        Calendar endLimit = Calendar.getInstance();
        endLimit.set(Calendar.HOUR_OF_DAY, endHour);
        endLimit.set(Calendar.MINUTE, endMinute);
        endLimit.set(Calendar.SECOND, 0);
        endLimit.set(Calendar.MILLISECOND, 0);

        while (cursor.before(endLimit)) {
            int h = cursor.get(Calendar.HOUR_OF_DAY);
            int m = cursor.get(Calendar.MINUTE);
            
            // Skip lunch break from 12:00 to 14:00
            if (h >= 12 && h < 14) {
                cursor.add(Calendar.MINUTE, 20);
                continue;
            }

            String timeStr = String.format(java.util.Locale.US, "%02d:%02d", h, m);

            if (!isToday || h > currentHour || (h == currentHour && m > currentMinute)) {
                addSlot(slots, timeStr, bookedTimes);
            }
            
            cursor.add(Calendar.MINUTE, 20);
        }

        return slots;
    }

    private void addSlot(List<TimeSlot> slots, String time, List<String> bookedTimes) {
        boolean isBooked = bookedTimes.contains(time);
        boolean isSelected = time.equals(selectedTime.getValue());
        slots.add(new TimeSlot(time, isBooked, isSelected));
    }

    public void confirmAppointment(String type, String reason) {
        String date = selectedDate.getValue();
        String time = selectedTime.getValue();

        if (auth.getCurrentUser() == null)
            return;
        String userId = auth.getCurrentUser().getUid();

        if (date != null && time != null) {
            saveError.setValue(null); // Resetar erro antes de validar

            // Validar no servidor se o horário já está ocupado por este médico
            db.collection("consultas")
                    .whereEqualTo("type", type)
                    .whereEqualTo("date", date)
                    .whereEqualTo("time", time)
                    .get()
                    .addOnSuccessListener(queryDocumentSnapshots -> {
                        if (!queryDocumentSnapshots.isEmpty()) {
                            // Já existe uma consulta!
                            saveError.postValue("Este horário já foi marcado por outro paciente. Por favor, escolha outro.");
                        } else {
                            // O horário está livre, prosseguir com a marcação
                            Appointment appointment = new Appointment(type, date, time, reason, false, userId, "Pendente");

                            db.collection("consultas")
                                    .add(appointment)
                                    .addOnSuccessListener(documentReference -> {
                                        try {
                                            String[] dateParts = date.split("/");
                                            int day = Integer.parseInt(dateParts[0]);
                                            int month = Integer.parseInt(dateParts[1]) - 1; // 0-based
                                            int year = Integer.parseInt(dateParts[2]);

                                            String[] timeParts = time.split(":");
                                            int hour = Integer.parseInt(timeParts[0]);
                                            int minute = Integer.parseInt(timeParts[1]);

                                            Calendar baseCal = Calendar.getInstance();
                                            baseCal.set(year, month, day, hour, minute, 0);

                                            // Schedule 24 hours before
                                            Calendar cal24h = (Calendar) baseCal.clone();
                                            cal24h.add(Calendar.DAY_OF_YEAR, -1);
                                            if (cal24h.getTimeInMillis() > System.currentTimeMillis()) {
                                                AlarmScheduler.scheduleAlarm(getApplication(), cal24h.getTimeInMillis(),
                                                        "Lembrete de Consulta", "A sua consulta é amanhã às " + time,
                                                        (date + time + "24h").hashCode());
                                            }

                                            // Schedule 30 minutes before
                                            Calendar cal30m = (Calendar) baseCal.clone();
                                            cal30m.add(Calendar.MINUTE, -30);
                                            if (cal30m.getTimeInMillis() > System.currentTimeMillis()) {
                                                AlarmScheduler.scheduleAlarm(getApplication(), cal30m.getTimeInMillis(),
                                                        "Lembrete de Consulta", "A sua consulta é daqui a 30 minutos (" + time + ")",
                                                        (date + time + "30m").hashCode());
                                            }

                                            // Notifications for morning appointments
                                            if (hour < 12) {
                                                // 3 hours before
                                                Calendar cal3h = (Calendar) baseCal.clone();
                                                cal3h.add(Calendar.HOUR_OF_DAY, -3);
                                                if (cal3h.getTimeInMillis() > System.currentTimeMillis()) {
                                                    AlarmScheduler.scheduleAlarm(getApplication(), cal3h.getTimeInMillis(),
                                                            "Lembrete de Consulta", "A sua consulta é daqui a 3 horas (" + time + ")",
                                                            (date + time + "3h").hashCode());
                                                }

                                                // 1 hour before
                                                Calendar cal1h = (Calendar) baseCal.clone();
                                                cal1h.add(Calendar.HOUR_OF_DAY, -1);
                                                if (cal1h.getTimeInMillis() > System.currentTimeMillis()) {
                                                    AlarmScheduler.scheduleAlarm(getApplication(), cal1h.getTimeInMillis(),
                                                            "Lembrete de Consulta", "A sua consulta é daqui a 1 hora (" + time + ")",
                                                            (date + time + "1h").hashCode());
                                                }
                                            }
                                        } catch (Exception e) {
                                            e.printStackTrace();
                                        }
                                        saveSuccess.postValue(true);
                                    })
                                    .addOnFailureListener(e -> Log.e("ScheduleViewModel", "Failed to confirm appt", e));
                }
            }).addOnFailureListener(e -> {
                saveError.postValue("Erro ao verificar a disponibilidade do horário. Tente novamente.");
            });
        }
    }
}
