package com.example.petstoremobile.fragments.listfragments.detailfragments;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.fragment.NavHostFragment;

import com.example.petstoremobile.databinding.FragmentAppointmentDetailBinding;
import com.example.petstoremobile.dtos.AppointmentDTO;
import com.example.petstoremobile.dtos.DropdownDTO;
import com.example.petstoremobile.dtos.ServiceDTO;
import com.example.petstoremobile.utils.DateTimeUtils;
import com.example.petstoremobile.utils.DialogUtils;
import com.example.petstoremobile.utils.InputValidator;
import com.example.petstoremobile.utils.Resource;
import com.example.petstoremobile.utils.SpinnerUtils;
import com.example.petstoremobile.utils.UIUtils;
import com.example.petstoremobile.api.auth.TokenManager;
import com.example.petstoremobile.viewmodels.AppointmentDetailViewModel;

import java.util.List;

import javax.inject.Inject;

import dagger.hilt.android.AndroidEntryPoint;

/**
 * Fragment for displaying and editing appointment details.
 */
@AndroidEntryPoint
public class AppointmentDetailFragment extends Fragment {

    private FragmentAppointmentDetailBinding binding;

    private final Integer[] HOURS = {9, 10, 11, 12, 13, 14, 15, 16, 17};
    private final Integer[] MINUTES = {0, 15, 30, 45};

    private AppointmentDetailViewModel viewModel;
    private boolean isUpdatingUI = false;

    @Inject TokenManager tokenManager;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        viewModel = new ViewModelProvider(this).get(AppointmentDetailViewModel.class);
    }

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        binding = FragmentAppointmentDetailBinding.inflate(inflater, container, false);
        return binding.getRoot();
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        setupSpinners();
        setupDatePicker();
        observeViewModel();
        viewModel.loadInitialFormData();
        handleArguments();

        binding.btnApptBack.setOnClickListener(v -> navigateBack());
        binding.btnSaveAppointment.setOnClickListener(v -> saveAppointment());
        binding.btnDeleteAppointment.setOnClickListener(v -> confirmDelete());
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        binding = null;
    }

    private void setupSpinners() {
        SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerAppointmentStatus, new String[]{});

        String[] hours = new String[HOURS.length];
        for (int i = 0; i < HOURS.length; i++)
            hours[i] = DateTimeUtils.formatTime(HOURS[i], 0);
        SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerHour, hours);
        SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerMinute, new String[]{"00", "15", "30", "45"});

        UIUtils.setViewsEnabled(false, binding.spinnerPet, binding.spinnerStaff);

        SpinnerUtils.setOnIndexSelectedListener(binding.spinnerCustomer, p -> {
            if (isUpdatingUI) return;
            viewModel.onCustomerSelected(p);
        });
        SpinnerUtils.setOnIndexSelectedListener(binding.spinnerStore, p -> {
            if (isUpdatingUI) return;
            viewModel.onStoreSelected(p);
        });

        SpinnerUtils.setOnIndexSelectedListener(binding.spinnerService, p -> viewModel.onServiceSelected(p));
        SpinnerUtils.setOnIndexSelectedListener(binding.spinnerPet, p -> viewModel.onPetSelected(p));
        SpinnerUtils.setOnIndexSelectedListener(binding.spinnerStaff, p -> viewModel.onStaffSelected(p));

        SpinnerUtils.setOnIndexSelectedListener(binding.spinnerHour, p -> notifyDateTimeStatusChange());
        SpinnerUtils.setOnIndexSelectedListener(binding.spinnerMinute, p -> notifyDateTimeStatusChange());
        SpinnerUtils.setOnIndexSelectedListener(binding.spinnerAppointmentStatus, p -> notifyDateTimeStatusChange());
    }

    private void setupDatePicker() {
        binding.etAppointmentDate.setOnClickListener(v ->
                UIUtils.showDatePicker(requireContext(), binding.etAppointmentDate, this::notifyDateTimeStatusChange));
    }

    private void observeViewModel() {
        viewModel.getViewState().observe(getViewLifecycleOwner(), this::applyViewState);

        viewModel.getCustomers().observe(getViewLifecycleOwner(), list -> {
            AppointmentDetailViewModel.ViewState state = viewModel.getViewState().getValue();
            Long id = state != null ? state.selectedCustomerId : null;
            SpinnerUtils.populateSpinner(requireContext(), binding.spinnerCustomer, list, DropdownDTO::getLabel, "-- Select Customer --", id, DropdownDTO::getId);
        });

        viewModel.getStores().observe(getViewLifecycleOwner(), list -> {
            AppointmentDetailViewModel.ViewState state = viewModel.getViewState().getValue();
            Long id = isStaff() ? tokenManager.getPrimaryStoreId() : (state != null ? state.selectedStoreId : null);
            SpinnerUtils.populateSpinner(requireContext(), binding.spinnerStore, list, DropdownDTO::getLabel, "-- Select Store --", id, DropdownDTO::getId);
        });

        viewModel.getServices().observe(getViewLifecycleOwner(), list -> {
            AppointmentDetailViewModel.ViewState state = viewModel.getViewState().getValue();
            Long id = state != null ? state.selectedServiceId : null;
            SpinnerUtils.populateSpinner(requireContext(), binding.spinnerService, list, ServiceDTO::getServiceName, "-- Select Service --", id, ServiceDTO::getServiceId);
        });

        viewModel.getCustomerPets().observe(getViewLifecycleOwner(), list -> {
            AppointmentDetailViewModel.ViewState state = viewModel.getViewState().getValue();
            Long id = state != null ? state.selectedPetId : null;
            SpinnerUtils.populateSpinner(requireContext(), binding.spinnerPet, list, DropdownDTO::getLabel, "-- Select Pet --", id, DropdownDTO::getId);
        });

        viewModel.getStoreEmployees().observe(getViewLifecycleOwner(), list -> {
            AppointmentDetailViewModel.ViewState state = viewModel.getViewState().getValue();
            Long id = state != null ? state.selectedStaffId : null;
            SpinnerUtils.populateSpinner(requireContext(), binding.spinnerStaff, list, DropdownDTO::getLabel, "-- Select Staff --", id, DropdownDTO::getId);
        });
    }

    private void setLoading(boolean loading) {
        if (binding != null && binding.progressBar != null) {
            binding.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
        }
    }

    private void applyViewState(AppointmentDetailViewModel.ViewState state) {
        isUpdatingUI = true;

        binding.tvApptMode.setText(state.isEditing ? "Edit Appointment" : "Add Appointment");
        binding.tvAppointmentId.setText(DateTimeUtils.formatId(viewModel.getAppointmentId()));
        binding.tvAppointmentId.setVisibility(state.isEditing ? View.VISIBLE : View.GONE);
        binding.btnDeleteAppointment.setVisibility(state.isDeleteVisible ? View.VISIBLE : View.GONE);
        binding.btnSaveAppointment.setVisibility(state.isSaveVisible ? View.VISIBLE : View.GONE);

        UIUtils.setFieldEnabled(state.isCustomerEnabled, binding.spinnerCustomer, binding.tvLabelCustomer);
        UIUtils.setFieldEnabled(state.isStoreEnabled, binding.spinnerStore, binding.tvLabelStore);
        UIUtils.setFieldEnabled(state.isPetEnabled, binding.spinnerPet, binding.tvLabelPet);
        UIUtils.setFieldEnabled(state.isServiceEnabled, binding.spinnerService, binding.tvLabelService);
        UIUtils.setFieldEnabled(state.isStaffEnabled, binding.spinnerStaff, binding.tvLabelStaff);
        UIUtils.setFieldEnabled(state.isDateEnabled, binding.etAppointmentDate, binding.tvLabelDate);
        UIUtils.setFieldEnabled(state.isTimeEnabled, binding.spinnerHour, binding.tvLabelTime);
        UIUtils.setViewsEnabled(state.isTimeEnabled, binding.spinnerMinute);
        UIUtils.setViewsEnabled(state.isStatusEnabled, binding.spinnerAppointmentStatus);

        SpinnerUtils.setupStringSpinner(requireContext(), binding.spinnerAppointmentStatus, state.availableStatuses);
        SpinnerUtils.setSelectionByValue(binding.spinnerAppointmentStatus, state.selectedStatus);

        // Re-populate dropdown spinners with current selected IDs from ViewState
        List<DropdownDTO> customers = viewModel.getCustomers().getValue();
        if (customers != null) SpinnerUtils.populateSpinner(requireContext(), binding.spinnerCustomer,
                customers, DropdownDTO::getLabel, "-- Select Customer --", state.selectedCustomerId, DropdownDTO::getId);

        List<DropdownDTO> stores = viewModel.getStores().getValue();
        if (stores != null) SpinnerUtils.populateSpinner(requireContext(), binding.spinnerStore,
                stores, DropdownDTO::getLabel, "-- Select Store --", state.selectedStoreId, DropdownDTO::getId);

        List<ServiceDTO> services = viewModel.getServices().getValue();
        if (services != null) SpinnerUtils.populateSpinner(requireContext(), binding.spinnerService,
                services, ServiceDTO::getServiceName, "-- Select Service --", state.selectedServiceId, ServiceDTO::getServiceId);

        List<DropdownDTO> pets = viewModel.getCustomerPets().getValue();
        if (pets != null) SpinnerUtils.populateSpinner(requireContext(), binding.spinnerPet,
                pets, DropdownDTO::getLabel, "-- Select Pet --", state.selectedPetId, DropdownDTO::getId);

        List<DropdownDTO> staff = viewModel.getStoreEmployees().getValue();
        if (staff != null) SpinnerUtils.populateSpinner(requireContext(), binding.spinnerStaff,
                staff, DropdownDTO::getLabel, "-- Select Staff --", state.selectedStaffId, DropdownDTO::getId);

        if (isStaff()) UIUtils.setViewsEnabled(false, binding.spinnerStore);

        isUpdatingUI = false;
    }

    private boolean isStaff() {
        return "STAFF".equalsIgnoreCase(tokenManager.getRole());
    }

    private void notifyDateTimeStatusChange() {
        if (isUpdatingUI) return;

        String date = binding.etAppointmentDate.getText().toString();
        String time = buildTimeString();
        Object selected = binding.spinnerAppointmentStatus.getSelectedItem();
        String status = selected != null ? selected.toString() : "";
        viewModel.onDateOrTimeChanged(date, time, status);
    }

    private void handleArguments() {
        Bundle a = getArguments();
        if (a != null && a.containsKey("appointmentId")) {
            viewModel.setAppointmentId(a.getLong("appointmentId"));
            loadAppointmentData();
        } else {
            viewModel.setAppointmentId(-1);
        }
    }

    private void loadAppointmentData() {
        viewModel.loadAppointment().observe(getViewLifecycleOwner(), resource -> {
            if (resource == null) return;
            setLoading(resource.status == Resource.Status.LOADING);
            if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
                AppointmentDTO a = resource.data;
                binding.etAppointmentDate.setText(a.getAppointmentDate());
                parseAndSetTimeSpinners(a.getAppointmentTime() != null ? a.getAppointmentTime() : "09:00");
                String status = a.getAppointmentStatus();
                if (status != null && !status.isEmpty()) {
                    SpinnerUtils.setSelectionByValue(binding.spinnerAppointmentStatus, DateTimeUtils.formatStatusFromBackend(status));
                }
                notifyDateTimeStatusChange();
            } else if (resource.status == Resource.Status.ERROR) {
                Toast.makeText(getContext(), "Failed to load appointment", Toast.LENGTH_SHORT).show();
            }
        });
    }

    private void saveAppointment() {
        if (!validateRequiredFields()) return;

        String date = binding.etAppointmentDate.getText().toString().trim();
        String time = buildTimeString();
        Object selectedStatus = binding.spinnerAppointmentStatus.getSelectedItem();
        if (selectedStatus == null) {
            Toast.makeText(getContext(), "Please select a status", Toast.LENGTH_SHORT).show();
            return;
        }
        String status = selectedStatus.toString().toUpperCase();

        if (!viewModel.isValidFutureBooking(status, date, time)) {
            DialogUtils.showInfoDialog(requireContext(), "Invalid Time", "Booked appointments must be in the future.");
            return;
        }

        viewModel.saveAppointment(date, time, status).observe(getViewLifecycleOwner(), resource -> {
            if (resource == null) return;
            setLoading(resource.status == Resource.Status.LOADING);
            if (resource.status == Resource.Status.SUCCESS) {
                AppointmentDetailViewModel.ViewState state = viewModel.getViewState().getValue();
                String message = (state != null && state.isEditing) ? "Updated" : "Saved";
                Toast.makeText(getContext(), message, Toast.LENGTH_SHORT).show();
                navigateBack();
            } else if (resource.status == Resource.Status.ERROR) {
                handleSaveError(resource.message);
            }
        });
    }

    private boolean validateRequiredFields() {
        if (!InputValidator.isSpinnerSelected(binding.spinnerCustomer, "Customer")) return false;
        if (!InputValidator.isSpinnerSelected(binding.spinnerStore, "Store")) return false;
        if (!InputValidator.isSpinnerSelected(binding.spinnerPet, "Pet")) return false;
        if (!InputValidator.isSpinnerSelected(binding.spinnerService, "Service")) return false;
        if (!InputValidator.isNotEmpty(binding.etAppointmentDate, "Appointment Date")) return false;
        return true;
    }

    private String buildTimeString() {
        return DateTimeUtils.formatTime(HOURS[binding.spinnerHour.getSelectedItemPosition()], MINUTES[binding.spinnerMinute.getSelectedItemPosition()]);
    }

    private void handleSaveError(String errorMessage) {
        if (errorMessage != null && errorMessage.toLowerCase().contains("not available")) showNoAvailabilityDialog();
        else Toast.makeText(getContext(), errorMessage != null ? errorMessage : "Error saving", Toast.LENGTH_SHORT).show();
    }

    private void showNoAvailabilityDialog() {
        new androidx.appcompat.app.AlertDialog.Builder(requireContext())
                .setTitle("No Availability")
                .setMessage("This time slot is already booked.")
                .setPositiveButton("Change Time", (d, w) -> d.dismiss())
                .setNegativeButton("Cancel Booking", (d, w) -> navigateBack()).show();
    }

    private void confirmDelete() {
        DialogUtils.showDeleteConfirmDialog(requireContext(), "Appointment", () ->
                viewModel.deleteAppointment().observe(getViewLifecycleOwner(), resource -> {
                    if (resource == null) return;
                    setLoading(resource.status == Resource.Status.LOADING);
                    if (resource.status == Resource.Status.SUCCESS) navigateBack();
                }));
    }

    private void navigateBack() {
        NavHostFragment.findNavController(this).popBackStack();
    }

    private void parseAndSetTimeSpinners(String time) {
        int[] parsedTime = DateTimeUtils.parseTimeString(time);
        if (parsedTime == null) return;
        SpinnerUtils.setSelectionByValueArray(binding.spinnerHour, HOURS, parsedTime[0]);
        SpinnerUtils.setSelectionByValueArray(binding.spinnerMinute, MINUTES, parsedTime[1]);
    }
}
