package com.example.petstoremobile.utils;

import android.content.Context;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Spinner;

import com.example.petstoremobile.adapters.BlackTextArrayAdapter;
import com.example.petstoremobile.adapters.WhiteTextArrayAdapter;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * Utility class for Spinners.
 */
public class SpinnerUtils {

    /**
     * Populates a spinner with a list of items and handles pre-selection.
     */
    public static <T> void populateSpinner(Context context, Spinner spinner, List<T> data,
                                           Function<T, String> nameExtractor, String defaultText,
                                           Long preselectedId, Function<T, Long> idExtractor) {
        populateSpinnerWithAdapter(context, spinner, data, nameExtractor, defaultText, preselectedId, idExtractor, false);
    }

    /**
     * Populates a spinner with white text (for dark backgrounds).
     */
    public static <T> void populateWhiteSpinner(Context context, Spinner spinner, List<T> data,
                                           Function<T, String> nameExtractor, String defaultText,
                                           Long preselectedId, Function<T, Long> idExtractor) {
        populateSpinnerWithAdapter(context, spinner, data, nameExtractor, defaultText, preselectedId, idExtractor, true);
    }

    private static <T> void populateSpinnerWithAdapter(Context context, Spinner spinner, List<T> data,
                                           Function<T, String> nameExtractor, String defaultText,
                                           Long preselectedId, Function<T, Long> idExtractor,
                                           boolean useWhiteText) {
        List<String> names = new ArrayList<>();
        if (defaultText != null) {
            names.add(defaultText);
        }
        
        for (T item : data) {
            names.add(nameExtractor.apply(item));
        }

        // Only update adapter if contents changed to remove infinite loop when spinner is opened
        if (isAdapterDataSame(spinner, names)) {
            setSelectedId(spinner, data, defaultText, preselectedId, idExtractor);
            return;
        }

        ArrayAdapter<String> adapter;
        if (useWhiteText) {
            adapter = new WhiteTextArrayAdapter<>(context, android.R.layout.simple_spinner_item, names);
        } else {
            adapter = new BlackTextArrayAdapter<>(context, android.R.layout.simple_spinner_item, names);
        }
        
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        spinner.setAdapter(adapter);

        setSelectedId(spinner, data, defaultText, preselectedId, idExtractor);
    }

    private static <T> void setSelectedId(Spinner spinner, List<T> data, String defaultText, Long preselectedId, Function<T, Long> idExtractor) {
        if (preselectedId != null && preselectedId != -1) {
            int offset = (defaultText != null) ? 1 : 0;
            for (int i = 0; i < data.size(); i++) {
                Long currentId = idExtractor.apply(data.get(i));
                if (Objects.equals(currentId, preselectedId)) {
                    if (spinner.getSelectedItemPosition() != i + offset) {
                        spinner.setSelection(i + offset);
                    }
                    break;
                }
            }
        }
    }

    /**
     * Checks if the adapter data is the same as the new data.
     */
    private static boolean isAdapterDataSame(Spinner spinner, List<String> newNames) {
        if (spinner.getAdapter() == null) return false;
        if (spinner.getAdapter().getCount() != newNames.size()) return false;
        for (int i = 0; i < newNames.size(); i++) {
            if (!Objects.equals(spinner.getAdapter().getItem(i), newNames.get(i))) return false;
        }
        return true;
    }

    /**
     * Sets up a simple string spinner for filtering with a callback.
     */
    public static void setupStringFilterSpinner(Context context, Spinner spinner, String[] items, Runnable onSelectionChanged) {
        updateStringSpinnerIfChanged(context, spinner, items, true);
        setupFilterSpinner(spinner, onSelectionChanged);
    }

    /**
     * Attaches an item selected listener to a spinner that triggers a callback.
     */
    public static void setupFilterSpinner(Spinner spinner, Runnable onSelectionChanged) {
        spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                onSelectionChanged.run();
            }
            @Override public void onNothingSelected(AdapterView<?> parent) {}
        });
    }

    /**
     * Sets a listener that provides the selected index to a consumer.
     */
    public static void setOnIndexSelectedListener(Spinner spinner, Consumer<Integer> callback) {
        spinner.setOnItemSelectedListener(new OnIndexSelected(callback));
    }

    /**
     * Sets the selection of a spinner based on a string value.
     */
    public static void setSelectionByValue(Spinner spinner, String value) {
        if (value == null || spinner.getAdapter() == null) return;
        ArrayAdapter<String> adapter = (ArrayAdapter<String>) spinner.getAdapter();
        int pos = adapter.getPosition(value);
        if (pos >= 0 && spinner.getSelectedItemPosition() != pos) {
            spinner.setSelection(pos);
        }
    }

    /**
     * Sets the selection of a spinner based on a value within an array.
     */
    public static <T> void setSelectionByValueArray(Spinner spinner, T[] array, T value) {
        if (spinner == null || array == null || value == null) return;
        for (int i = 0; i < array.length; i++) {
            if (Objects.equals(array[i], value)) {
                if (spinner.getSelectedItemPosition() != i) {
                    spinner.setSelection(i);
                }
                return;
            }
        }
    }

    /**
     * Configures a simple string array spinner.
     */
    public static void setupStringSpinner(Context context, Spinner spinner, String[] items) {
        updateStringSpinnerIfChanged(context, spinner, items, false);
    }

    /**
     * Updates a string spinner only if the items have changed.
     */
    public static void updateStringSpinnerIfChanged(Context context, Spinner spinner, String[] items, boolean useWhiteText) {
        if (isAdapterDataSame(spinner, Arrays.asList(items))) return;

        ArrayAdapter<String> adapter;
        if (useWhiteText) {
            adapter = new WhiteTextArrayAdapter<>(context, android.R.layout.simple_spinner_item, items);
        } else {
            adapter = new BlackTextArrayAdapter<>(context, android.R.layout.simple_spinner_item, items);
        }
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        spinner.setAdapter(adapter);
    }

    /**
     * Helper listener to get selected index from a spinner.
     */
    public static class OnIndexSelected implements AdapterView.OnItemSelectedListener {
        private final Consumer<Integer> callback;

        public OnIndexSelected(Consumer<Integer> callback) {
            this.callback = callback;
        }

        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            callback.accept(position);
        }

        @Override
        public void onNothingSelected(AdapterView<?> parent) {}
    }
}
