package it.integry.integrywmsnative.gest.vendita.dialogs;

import android.app.AlertDialog;
import android.app.DatePickerDialog;
import android.content.Context;

import androidx.databinding.DataBindingUtil;

import android.view.LayoutInflater;
import android.widget.ArrayAdapter;
import android.widget.Button;

import com.annimon.stream.Stream;
import com.annimon.stream.function.Predicate;

import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;

import it.integry.integrywmsnative.R;
import it.integry.integrywmsnative.core.di.BindableString;
import it.integry.integrywmsnative.core.expansion.RunnableArgss;
import it.integry.integrywmsnative.core.utility.UtilityDate;
import it.integry.integrywmsnative.core.utility.UtilityString;
import it.integry.integrywmsnative.databinding.DialogVenditaFiltroAvanzatoBinding;
import it.integry.integrywmsnative.gest.vendita.dto.OrdineVenditaInevasoDTO;


public class DialogVenditaFiltroAvanzato {


    private AlertDialog currentAlert;
    private Context currentContext;

    private RunnableArgss<List<OrdineVenditaInevasoDTO>, DialogVenditaFiltroAvanzatoViewModel> currentOnFilterDone;

    private List<OrdineVenditaInevasoDTO> currentOrderList;
    private List<OrdineVenditaInevasoDTO> currentFilteredOrderList;

    private Predicate<OrdineVenditaInevasoDTO> currentNumOrdsPredicate = null;
    private Predicate<OrdineVenditaInevasoDTO> currentClientePredicate = null;
    private Predicate<OrdineVenditaInevasoDTO> currentDataConsPredicate = null;
    private Predicate<OrdineVenditaInevasoDTO> currentTermConsPredicate = null;
    private Predicate<OrdineVenditaInevasoDTO> currentVettorePredicate = null;
    private Predicate<OrdineVenditaInevasoDTO> currentAutomezzoPredicate = null;

    private boolean firstInit = true;

    public static AlertDialog make(final Context context, final List<OrdineVenditaInevasoDTO> ordersList, RunnableArgss<List<OrdineVenditaInevasoDTO>, DialogVenditaFiltroAvanzatoViewModel> onDismiss) {
        return new DialogVenditaFiltroAvanzato(context, ordersList, null, onDismiss).currentAlert;
    }

    public static AlertDialog make(final Context context,
                                   final List<OrdineVenditaInevasoDTO> ordersList,
                                   DialogVenditaFiltroAvanzatoViewModel baseViewModel,
                                   RunnableArgss<List<OrdineVenditaInevasoDTO>, DialogVenditaFiltroAvanzatoViewModel> onDismiss) {
        return new DialogVenditaFiltroAvanzato(context, ordersList, baseViewModel, onDismiss).currentAlert;
    }

    private DialogVenditaFiltroAvanzato(final Context context, final List<OrdineVenditaInevasoDTO> ordersList, DialogVenditaFiltroAvanzatoViewModel baseViewModel, RunnableArgss<List<OrdineVenditaInevasoDTO>, DialogVenditaFiltroAvanzatoViewModel> onDismiss) {
        currentContext = context;
        currentOrderList = ordersList;
        currentOnFilterDone = onDismiss;

        DialogVenditaFiltroAvanzatoViewModel viewModel = baseViewModel != null ? baseViewModel : new DialogVenditaFiltroAvanzatoViewModel();

        LayoutInflater inflater = (LayoutInflater) context.getSystemService( Context.LAYOUT_INFLATER_SERVICE );

        DialogVenditaFiltroAvanzatoBinding contentView = DataBindingUtil.inflate(inflater, R.layout.dialog_vendita_filtro_avanzato, null, false);

        final AlertDialog.Builder alertDialog = new AlertDialog.Builder(context)
                .setView(contentView.getRoot())
                .setPositiveButton(context.getText(R.string.confirm), null)
                .setNegativeButton(context.getText(R.string.abort), null)
                .setNeutralButton(context.getText(R.string.reset), null);

        contentView.setViewmodel(viewModel);
        initViewModel(viewModel, contentView);
        initView(contentView, viewModel);

        currentAlert = alertDialog.create();
        currentAlert.setCanceledOnTouchOutside(false);



        currentAlert.setOnShowListener(dialogInterface -> {

            Button button = ((AlertDialog) dialogInterface).getButton(AlertDialog.BUTTON_NEUTRAL);
            button.setOnClickListener(view -> {
                // TODO Do something
                resetAll(viewModel);
            });
        });


        currentAlert.setOnDismissListener(dialogInterface -> {
            if(currentOnFilterDone != null) currentOnFilterDone.run(currentFilteredOrderList, viewModel);
        });

        refreshList();
    }

    private void resetAll(DialogVenditaFiltroAvanzatoViewModel viewModel) {
        viewModel.numOrds.set(null);
        viewModel.numOrds.set(null);
        viewModel.cliente.set(null);
        viewModel.dataConsegna.set(null);
        viewModel.terminiConsegna.set(null);
        viewModel.vettore.set(null);
        viewModel.automezzo.set(null);
    }

    private void initView(DialogVenditaFiltroAvanzatoBinding bindings, DialogVenditaFiltroAvanzatoViewModel viewModel) {

        bindings.inputDataCons.setOnClickListener(view -> {
            Calendar c = Calendar.getInstance();

            if(viewModel.dataConsegnaDate != null) {
                c.setTime(viewModel.dataConsegnaDate);
            }

            int mYear = c.get(Calendar.YEAR);
            int mMonth = c.get(Calendar.MONTH);
            int mDay = c.get(Calendar.DAY_OF_MONTH);

            DatePickerDialog datePickerDialog = new DatePickerDialog(currentContext,
                    (view2, year, month, day) -> {

                        viewModel.dataConsegnaDate = new GregorianCalendar(year, month, day).getTime();
                        viewModel.dataConsegna.set(UtilityDate.formatDate(viewModel.dataConsegnaDate, UtilityDate.COMMONS_DATE_FORMATS.DMY_HUMAN));

                    }, mYear, mMonth, mDay);
            datePickerDialog.show();
        });

        bindings.inputTermCons.setOnClickListener(view -> {

            AlertDialog.Builder builderSingle = new AlertDialog.Builder(currentContext);
            builderSingle.setTitle(R.string.terms_of_delivery);

            final ArrayAdapter<String> arrayAdapter = new ArrayAdapter(currentContext, android.R.layout.select_dialog_singlechoice);
            arrayAdapter.addAll(getAvailableTermCons());

            builderSingle.setAdapter(arrayAdapter, (dialogInterface, i) -> {
                viewModel.terminiConsegna.set(arrayAdapter.getItem(i));
            });
            builderSingle.show();

        });

        bindings.inputVettore.setOnClickListener(view -> {

            AlertDialog.Builder builderSingle = new AlertDialog.Builder(currentContext);
            builderSingle.setTitle(R.string.carrier);

            final ArrayAdapter<String> arrayAdapter = new ArrayAdapter(currentContext, android.R.layout.select_dialog_singlechoice);
            arrayAdapter.addAll(getAvailableVettori());

            builderSingle.setAdapter(arrayAdapter, (dialogInterface, i) -> {
                viewModel.vettore.set(arrayAdapter.getItem(i));
            });
            builderSingle.show();

        });

        bindings.inputAutomezzo.setOnClickListener(view -> {

            AlertDialog.Builder builderSingle = new AlertDialog.Builder(currentContext);
            builderSingle.setTitle(R.string.vehicle);

            final ArrayAdapter<String> arrayAdapter = new ArrayAdapter(currentContext, android.R.layout.select_dialog_singlechoice);
            arrayAdapter.addAll(getAvailableAutomezzi());

            builderSingle.setAdapter(arrayAdapter, (dialogInterface, i) -> {
                viewModel.automezzo.set(arrayAdapter.getItem(i));
            });
            builderSingle.show();

        });

    }

    private void initViewModel(DialogVenditaFiltroAvanzatoViewModel viewModel, DialogVenditaFiltroAvanzatoBinding bindings) {

        firstInit = true;

        BindableString.resetListeners(viewModel.numOrds);
        BindableString.registerListener(viewModel.numOrds, value -> {
            if(UtilityString.isNullOrEmpty(value)) currentNumOrdsPredicate = null;
            else {
                //String numOrdTrimmed = value.replaceAll(" ", "");
                String[] numOrdsSplitted = value.split("[,\\ ]");


                try{
                    List<Integer> numOrdsInteger = Stream.of(numOrdsSplitted)
                            .filter(x -> !UtilityString.isNullOrEmpty(x))
                            .map(Integer::parseInt).toList();

                    currentNumOrdsPredicate = o -> numOrdsInteger.contains(o.getNumOrd());

                    bindings.inputNumOrds.setErrorEnabled(false);
                    bindings.inputNumOrds.setError("");
                } catch (NumberFormatException ex) {
                    currentNumOrdsPredicate = null;

                    bindings.inputNumOrds.setErrorEnabled(true);
                    bindings.inputNumOrds.setError(currentContext.getResources().getString(R.string.error));
                }

            }

            if(!firstInit) refreshList();
        });
        viewModel.numOrds.refresh();


        BindableString.resetListeners(viewModel.cliente);
        BindableString.registerListener(viewModel.cliente, value -> {
            if(UtilityString.isNullOrEmpty(value)) currentClientePredicate = null;
            else {
                currentClientePredicate = o -> o.getRagSocOrd().toLowerCase().contains(value);
            }

            if(!firstInit) refreshList();
        });
        viewModel.cliente.refresh();




        BindableString.resetListeners(viewModel.dataConsegna);
        BindableString.registerListener(viewModel.dataConsegna, value -> {
            if(UtilityString.isNullOrEmpty(value)) currentDataConsPredicate = null;
            else {
                currentDataConsPredicate = o -> o.getDataConsD().equals(viewModel.dataConsegnaDate);
            }

            if(!firstInit) refreshList();
        });
        viewModel.dataConsegna.refresh();




        BindableString.resetListeners(viewModel.terminiConsegna);
        BindableString.registerListener(viewModel.terminiConsegna, value -> {
            if(UtilityString.isNullOrEmpty(value)) currentTermConsPredicate = null;
            else {
                currentTermConsPredicate = o -> o.getTermCons() != null && o.getTermCons().equalsIgnoreCase(value);
            }

            if(!firstInit) refreshList();
        });
        viewModel.terminiConsegna.refresh();


        BindableString.resetListeners(viewModel.vettore);
        BindableString.registerListener(viewModel.vettore, value -> {
            if(UtilityString.isNullOrEmpty(value)) currentVettorePredicate = null;
            else {
                currentVettorePredicate = o -> (o.getCodVvet() + " - " + o.getDescrizioneVettore()).equalsIgnoreCase(value);
            }

            if(!firstInit) refreshList();
        });
        viewModel.vettore.refresh();



        BindableString.resetListeners(viewModel.automezzo);
        BindableString.registerListener(viewModel.automezzo, value -> {
            if(UtilityString.isNullOrEmpty(value)) currentAutomezzoPredicate = null;
            else {
                currentAutomezzoPredicate = o -> o.getDescrizioneAuto().toLowerCase().contains(value);
            }

            if(!firstInit) refreshList();
        });
        viewModel.automezzo.refresh();

        if(firstInit) refreshList();
        firstInit = false;


    }

    private List<String> getAvailableTermCons() {
        if(currentNumOrdsPredicate == null && currentClientePredicate == null && currentDataConsPredicate == null && currentAutomezzoPredicate == null && currentVettorePredicate == null){
            currentFilteredOrderList = currentOrderList;
        } else {
            Stream<OrdineVenditaInevasoDTO> tmpStream = Stream.of(currentOrderList)
                    .filter(x ->
                            (currentNumOrdsPredicate == null || (currentNumOrdsPredicate != null && currentNumOrdsPredicate.test(x))) &&
                            (currentClientePredicate == null || (currentClientePredicate != null && currentClientePredicate.test(x))) &&
                            (currentDataConsPredicate == null || (currentDataConsPredicate != null && currentDataConsPredicate.test(x))) &&
                            (currentVettorePredicate == null || (currentVettorePredicate != null && currentVettorePredicate.test(x)))  &&
                            (currentAutomezzoPredicate == null || (currentAutomezzoPredicate != null && currentAutomezzoPredicate.test(x)))
                    );

            currentFilteredOrderList = tmpStream.toList();
        }

        return Stream.of(currentFilteredOrderList).map(OrdineVenditaInevasoDTO::getTermCons).distinct().withoutNulls().toList();
    }

    private List<String> getAvailableVettori() {
        if(currentNumOrdsPredicate == null && currentClientePredicate == null && currentDataConsPredicate == null && currentTermConsPredicate == null && currentAutomezzoPredicate == null){
            currentFilteredOrderList = currentOrderList;
        } else {
            Stream<OrdineVenditaInevasoDTO> tmpStream = Stream.of(currentOrderList)
                    .filter(x ->
                            (currentNumOrdsPredicate == null || (currentNumOrdsPredicate != null && currentNumOrdsPredicate.test(x))) &&
                                    (currentClientePredicate == null || (currentClientePredicate != null && currentClientePredicate.test(x))) &&
                                    (currentDataConsPredicate == null || (currentDataConsPredicate != null && currentDataConsPredicate.test(x))) &&
                                    (currentTermConsPredicate == null || (currentTermConsPredicate != null && currentTermConsPredicate.test(x))) &&
                                    (currentAutomezzoPredicate == null || (currentAutomezzoPredicate  != null && currentAutomezzoPredicate .test(x)))
                    );

            currentFilteredOrderList = tmpStream.toList();
        }

        return Stream.of(currentFilteredOrderList).map(x -> x.getCodVvet() + " - " + x.getDescrizioneVettore()).distinct().withoutNulls().toList();
    }

    private List<String> getAvailableAutomezzi() {
        if(currentNumOrdsPredicate == null && currentClientePredicate == null && currentDataConsPredicate == null && currentTermConsPredicate == null && currentVettorePredicate == null){
            currentFilteredOrderList = currentOrderList;
        } else {
            Stream<OrdineVenditaInevasoDTO> tmpStream = Stream.of(currentOrderList)
                    .filter(x ->
                            (currentNumOrdsPredicate == null || (currentNumOrdsPredicate != null && currentNumOrdsPredicate.test(x))) &&
                                    (currentClientePredicate == null || (currentClientePredicate != null && currentClientePredicate.test(x))) &&
                                    (currentDataConsPredicate == null || (currentDataConsPredicate != null && currentDataConsPredicate.test(x))) &&
                                    (currentTermConsPredicate == null || (currentTermConsPredicate != null && currentTermConsPredicate.test(x))) &&
                                    (currentVettorePredicate == null || (currentVettorePredicate != null && currentVettorePredicate.test(x)))
                    );

            currentFilteredOrderList = tmpStream.toList();
        }

        return Stream.of(currentFilteredOrderList).map(OrdineVenditaInevasoDTO::getDescrizioneAuto).distinct().withoutNulls().toList();
    }

    private void refreshList() {

        if(currentNumOrdsPredicate == null && currentClientePredicate == null && currentDataConsPredicate == null && currentTermConsPredicate == null && currentVettorePredicate == null && currentAutomezzoPredicate == null){
            currentFilteredOrderList = currentOrderList;
        } else {
            Stream<OrdineVenditaInevasoDTO> tmpStream = Stream.of(currentOrderList)
                .filter(x ->
                            (currentNumOrdsPredicate == null || (currentNumOrdsPredicate != null && currentNumOrdsPredicate.test(x))) &&
                            (currentClientePredicate == null || (currentClientePredicate != null && currentClientePredicate.test(x))) &&
                            (currentDataConsPredicate == null || (currentDataConsPredicate != null && currentDataConsPredicate.test(x))) &&
                            (currentTermConsPredicate == null || (currentTermConsPredicate != null && currentTermConsPredicate.test(x))) &&
                            (currentVettorePredicate == null || (currentVettorePredicate != null && currentVettorePredicate.test(x))) &&
                            (currentAutomezzoPredicate == null || (currentAutomezzoPredicate != null && currentAutomezzoPredicate.test(x)))
                );

            currentFilteredOrderList = tmpStream.toList();
        }


    }

}
