package com.example.petstoremobile.viewmodels;

import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;

import com.example.petstoremobile.dtos.AppointmentDTO;
import com.example.petstoremobile.dtos.DropdownDTO;
import com.example.petstoremobile.dtos.ServiceDTO;
import com.example.petstoremobile.repositories.AppointmentRepository;
import com.example.petstoremobile.repositories.CustomerRepository;
import com.example.petstoremobile.repositories.PetRepository;
import com.example.petstoremobile.repositories.ServiceRepository;
import com.example.petstoremobile.repositories.StoreRepository;
import com.example.petstoremobile.utils.DateTimeUtils;
import com.example.petstoremobile.utils.Resource;

import androidx.lifecycle.Observer;

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

import javax.inject.Inject;

import dagger.hilt.android.lifecycle.HiltViewModel;

/**
 * ViewModel for managing appointment details and form state.
 */
@HiltViewModel
public class AppointmentDetailViewModel extends ViewModel {
    private final AppointmentRepository repository;
    private final CustomerRepository customerRepository;
    private final StoreRepository storeRepository;
    private final PetRepository petRepository;
    private final ServiceRepository serviceRepository;

    private final MutableLiveData<List<DropdownDTO>> customers = new MutableLiveData<>();
    private final MutableLiveData<List<DropdownDTO>> stores = new MutableLiveData<>();
    private final MutableLiveData<List<ServiceDTO>> services = new MutableLiveData<>();
    private final MutableLiveData<List<DropdownDTO>> customerPets = new MutableLiveData<>();
    private final MutableLiveData<List<DropdownDTO>> storeEmployees = new MutableLiveData<>();
    private final MutableLiveData<ViewState> viewState = new MutableLiveData<>(new ViewState());

    private long appointmentId = -1;
    private boolean isOriginallyCancel = false;
    private boolean isOriginallyCompletedOrMissed = false;
    private Long currentCustomerId;
    private Long currentStoreId;
    private Long currentPetId;
    private Long currentServiceId;
    private Long currentStaffId;

    /**
     * Constructor for AppointmentDetailViewModel.
     */
    @Inject
    public AppointmentDetailViewModel(
            AppointmentRepository repository,
            CustomerRepository customerRepository,
            StoreRepository storeRepository,
            PetRepository petRepository,
            ServiceRepository serviceRepository) {
        this.repository = repository;
        this.customerRepository = customerRepository;
        this.storeRepository = storeRepository;
        this.petRepository = petRepository;
        this.serviceRepository = serviceRepository;
    }

    // Initial Data Loading

    /**
     * Loads initial dropdown data for customers, stores, and services.
     */
    public void loadInitialFormData() {
        observeOnce(customerRepository.getCustomerDropdowns(), r -> {
            if (r != null && r.status == Resource.Status.SUCCESS) customers.setValue(r.data);
        });
        observeOnce(storeRepository.getStoreDropdowns(), r -> {
            if (r != null && r.status == Resource.Status.SUCCESS) stores.setValue(r.data);
        });
        observeOnce(serviceRepository.getAllServices(0, 200, null, "serviceName"), r -> {
            if (r != null && r.status == Resource.Status.SUCCESS && r.data != null) services.setValue(r.data.getContent());
        });
    }

    // LiveData Getters

    /**
     * Returns the LiveData for the list of customers.
     */
    public LiveData<List<DropdownDTO>> getCustomers() { return customers; }

    /**
     * Returns the LiveData for the list of stores.
     */
    public LiveData<List<DropdownDTO>> getStores() { return stores; }

    /**
     * Returns the LiveData for the list of services.
     */
    public LiveData<List<ServiceDTO>> getServices() { return services; }

    /**
     * Returns the LiveData for the list of pets for the current customer.
     */
    public LiveData<List<DropdownDTO>> getCustomerPets() { return customerPets; }

    /**
     * Returns the LiveData for the list of employees for the current store.
     */
    public LiveData<List<DropdownDTO>> getStoreEmployees() { return storeEmployees; }

    /**
     * Returns the LiveData for the view state.
     */
    public LiveData<ViewState> getViewState() { return viewState; }

    //State Getters

    /**
     * Returns the current appointment ID.
     */
    public long getAppointmentId() { return appointmentId; }

    /**
     * Sets the current appointment ID and updates the mode.
     */
    public void setAppointmentId(long id) {
        this.appointmentId = id;
        initMode(id != -1);
    }

    // Selection Handlers for spinners

    /**
     * Handles customer selection and loads their pets.
     */
    public void onCustomerSelected(int position) {
        List<DropdownDTO> list = customers.getValue();
        if (position > 0 && list != null && position <= list.size()) {
            currentCustomerId = list.get(position - 1).getId();
            loadPetsForCustomer(currentCustomerId);
            updateViewState(s -> {
                s.selectedCustomerId = currentCustomerId;
                s.isPetEnabled = !s.isEditing;
            });
        } else {
            currentCustomerId = null;
            customerPets.setValue(new ArrayList<>());
            updateViewState(s -> {
                s.selectedCustomerId = null;
                s.isPetEnabled = false;
            });
        }
    }

    /**
     * Handles store selection and loads its employees.
     */
    public void onStoreSelected(int position) {
        List<DropdownDTO> list = stores.getValue();
        if (position > 0 && list != null && position <= list.size()) {
            currentStoreId = list.get(position - 1).getId();
            loadEmployeesForStore(currentStoreId);
            updateViewState(s -> {
                s.selectedStoreId = currentStoreId;
                s.isStaffEnabled = !s.isPast;
            });
        } else {
            currentStoreId = null;
            storeEmployees.setValue(new ArrayList<>());
            updateViewState(s -> {
                s.selectedStoreId = null;
                s.isStaffEnabled = false;
            });
        }
    }

    /**
     * Handles service selection.
     */
    public void onServiceSelected(int position) {
        List<ServiceDTO> list = services.getValue();
        currentServiceId = (position > 0 && list != null && position <= list.size()) ? list.get(position - 1).getServiceId() : null;
        updateViewState(s -> s.selectedServiceId = currentServiceId);
    }

    /**
     * Handles pet selection.
     */
    public void onPetSelected(int position) {
        List<DropdownDTO> list = customerPets.getValue();
        currentPetId = (position > 0 && list != null && position <= list.size()) ? list.get(position - 1).getId() : null;
        updateViewState(s -> s.selectedPetId = currentPetId);
    }

    /**
     * Handles staff selection.
     */
    public void onStaffSelected(int position) {
        List<DropdownDTO> list = storeEmployees.getValue();
        currentStaffId = (position > 0 && list != null && position <= list.size()) ? list.get(position - 1).getId() : null;
        updateViewState(s -> s.selectedStaffId = currentStaffId);
    }

    /**
     * Loads the list of pets for a specific customer.
     */
    private void loadPetsForCustomer(Long customerId) {
        observeOnce(petRepository.getCustomerPets(customerId), r -> {
            if (r != null && r.status == Resource.Status.SUCCESS) customerPets.setValue(r.data);
        });
    }

    /**
     * Loads the list of employees for a specific store.
     */
    private void loadEmployeesForStore(Long storeId) {
        observeOnce(storeRepository.getStoreEmployees(storeId), r -> {
            if (r != null && r.status == Resource.Status.SUCCESS) storeEmployees.setValue(r.data);
        });
    }

    // Appointment Detail CRUD

    /**
     * Fetches appointment details and populates internal state.
     */
    public LiveData<Resource<AppointmentDTO>> loadAppointment() {
        MutableLiveData<Resource<AppointmentDTO>> result = new MutableLiveData<>();
        observeOnce(repository.getAppointmentById(appointmentId), resource -> {
            if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
                AppointmentDTO a = resource.data;
                isOriginallyCancel = "CANCELLED".equalsIgnoreCase(a.getAppointmentStatus());
                isOriginallyCompletedOrMissed = "COMPLETED".equalsIgnoreCase(a.getAppointmentStatus())
                        || "MISSED".equalsIgnoreCase(a.getAppointmentStatus());
                currentCustomerId = a.getCustomerId();
                currentStoreId = a.getStoreId();
                currentPetId = a.getPetId();
                currentServiceId = a.getServiceId();
                currentStaffId = a.getEmployeeId();

                String formattedStatus = DateTimeUtils.formatStatusFromBackend(a.getAppointmentStatus());
                updateViewState(s -> {
                    s.selectedCustomerId = currentCustomerId;
                    s.selectedStoreId = currentStoreId;
                    s.selectedPetId = currentPetId;
                    s.selectedServiceId = currentServiceId;
                    s.selectedStaffId = currentStaffId;
                    s.selectedStatus = formattedStatus;
                });

                if (currentCustomerId != null) loadPetsForCustomer(currentCustomerId);
                if (currentStoreId != null) loadEmployeesForStore(currentStoreId);
            }
            result.setValue(resource);
        });
        return result;
    }

    /**
     * Saves the appointment by building the DTO from tracked state.
     */
    public LiveData<Resource<AppointmentDTO>> saveAppointment(String date, String time, String status) {
        AppointmentDTO dto = new AppointmentDTO(currentCustomerId, currentStoreId, currentServiceId, currentStaffId, date, time, status, currentPetId);
        if (appointmentId != -1) {
            return repository.updateAppointment(appointmentId, dto);
        } else {
            return repository.createAppointment(dto);
        }
    }

    /**
     * Deletes the current appointment.
     */
    public LiveData<Resource<Void>> deleteAppointment() {
        return repository.deleteAppointment(appointmentId);
    }

    // UI Logic

    /**
     * Updates the UI state when date, time, or status changes.
     */
    public void onDateOrTimeChanged(String date, String time, String currentStatus) {
        updateViewState(s -> {
            s.availableStatuses = calculateAvailableStatuses(s.isEditing, date, time, currentStatus);
            java.util.List<String> available = java.util.Arrays.asList(s.availableStatuses);
            if (!currentStatus.isEmpty() && available.contains(currentStatus)) {
                s.selectedStatus = currentStatus;
            } else if (!available.contains(s.selectedStatus) && s.availableStatuses.length > 0) {
                s.selectedStatus = s.availableStatuses[0];
            }
            if (isOriginallyCancel) {
                s.isPast = true;
                setAllFieldsEnabled(s, false);
                s.isStatusEnabled = false;
                s.isSaveVisible = false;
            } else if (isOriginallyCompletedOrMissed) {
                s.isPast = true;
                setAllFieldsEnabled(s, false);
                s.isStatusEnabled = true;
            } else {
                s.isPast = false;
                if (!s.isEditing) {
                    s.isCustomerEnabled = true;
                    s.isStoreEnabled = true;
                    s.isServiceEnabled = true;
                    s.isPetEnabled = currentCustomerId != null;
                }
                s.isDateEnabled = true;
                s.isTimeEnabled = true;
                s.isStatusEnabled = true;
            }
        });
    }

    /**
     * Calculates available appointment statuses based on the current context.
     */
    private String[] calculateAvailableStatuses(boolean isEditing, String date, String currentTime, String currentStatus) {
        if (!isEditing) return new String[]{"Booked"};
        if (date == null || date.isEmpty()) return new String[]{};
        if (isOriginallyCancel) return new String[]{"Cancelled"};
        if (isOriginallyCompletedOrMissed) return new String[]{"Completed", "Missed"};
        return new String[]{"Booked", "Cancelled"};
    }

    /**
     * Helper method to enable or disable all fields.
     */
    private void setAllFieldsEnabled(ViewState s, boolean enabled) {
        s.isCustomerEnabled = enabled;
        s.isStoreEnabled = enabled;
        s.isPetEnabled = enabled;
        s.isServiceEnabled = enabled;
        s.isStaffEnabled = enabled;
        s.isDateEnabled = enabled;
        s.isTimeEnabled = enabled;
    }

    /**
     * Initializes the UI mode (Create vs Edit).
     */
    public void initMode(boolean isEditing) {
        updateViewState(s -> {
            s.isEditing = isEditing;
            s.isDeleteVisible = isEditing;
            if (isEditing) {
                s.isCustomerEnabled = false;
                s.isStoreEnabled = false;
                s.isPetEnabled = false;
                s.isServiceEnabled = false;
            } else {
                s.isCustomerEnabled = true;
                s.isStoreEnabled = true;
                s.isServiceEnabled = true;
                s.isPetEnabled = false;
                s.isStaffEnabled = false;
                s.availableStatuses = new String[]{"Booked"};
                s.selectedStatus = "Booked";
            }
        });
    }

    /**
     * Validates if a booking is in the future.
     */
    public boolean isValidFutureBooking(String status, String date, String time) {
        return !"BOOKED".equalsIgnoreCase(status) || !DateTimeUtils.isDateTimeInPast(date, time);
    }

    /**
     * Helper to update the view state and notify observers.
     */
    private void updateViewState(Action<ViewState> action) {
        ViewState current = viewState.getValue();
        if (current != null) {
            action.run(current);
            viewState.setValue(current);
        }
    }

    private interface Action<T> {
        void run(T t);
    }

    /** Observes a LiveData once, removing the observer after the first non-loading response. */
    private <T> void observeOnce(LiveData<Resource<T>> liveData, Observer<Resource<T>> handler) {
        liveData.observeForever(new Observer<Resource<T>>() {
            @Override
            public void onChanged(Resource<T> resource) {
                if (resource == null || resource.status != Resource.Status.LOADING) {
                    liveData.removeObserver(this);
                }
                handler.onChanged(resource);
            }
        });
    }

    /**
     * A Class to show the states of Appointment Detail Fragment.
     */
    public static class ViewState {
        public boolean isPast = false;
        public boolean isEditing = false;
        public boolean isSaveVisible = true;
        public boolean isDeleteVisible = false;
        public boolean isCustomerEnabled = true;
        public boolean isStoreEnabled = true;
        public boolean isPetEnabled = false;
        public boolean isServiceEnabled = true;
        public boolean isStaffEnabled = false;
        public boolean isDateEnabled = true;
        public boolean isTimeEnabled = true;
        public boolean isStatusEnabled = true;
        public String[] availableStatuses = new String[]{};
        public String selectedStatus = "";

        // Selected IDs
        public Long selectedCustomerId = null;
        public Long selectedStoreId = null;
        public Long selectedPetId = null;
        public Long selectedServiceId = null;
        public Long selectedStaffId = null;
    }
}
