package it.integry.integrywmsnative.gest.prod_rientro_merce.order_detail;

import android.bluetooth.BluetoothDevice;
import android.content.SharedPreferences;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;

import androidx.core.content.ContextCompat;
import androidx.databinding.DataBindingUtil;
import androidx.databinding.ObservableArrayList;
import androidx.databinding.ObservableField;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.MutableLiveData;
import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.LinearLayoutManager;

import com.annimon.stream.ComparatorCompat;
import com.annimon.stream.Stream;
import com.google.gson.reflect.TypeToken;
import com.harrysoft.androidbluetoothserial.BluetoothManager;
import com.harrysoft.androidbluetoothserial.BluetoothSerialDevice;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

import javax.inject.Inject;

import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import it.integry.integrywmsnative.MainApplication;
import it.integry.integrywmsnative.R;
import it.integry.integrywmsnative.core.exception.BluetoothConnectionException;
import it.integry.integrywmsnative.core.expansion.AtomicBigDecimal;
import it.integry.integrywmsnative.core.expansion.BaseFragment;
import it.integry.integrywmsnative.core.expansion.RunnableArgs;
import it.integry.integrywmsnative.core.model.DtbOrdSteps;
import it.integry.integrywmsnative.core.model.MtbColr;
import it.integry.integrywmsnative.core.model.MtbColt;
import it.integry.integrywmsnative.core.rest.consumers.ArticoloRESTConsumer;
import it.integry.integrywmsnative.core.rest.consumers.SystemRESTConsumer;
import it.integry.integrywmsnative.core.utility.UtilityDB;
import it.integry.integrywmsnative.core.utility.UtilityString;
import it.integry.integrywmsnative.databinding.FragmentProdRientroMerceOrderDetailBinding;
import it.integry.integrywmsnative.gest.prod_versamento_materiale.dto.OrdineLavorazioneDTO;
import it.integry.integrywmsnative.gest.settings.MainSettingsFragment;
import it.integry.integrywmsnative.ui.SimpleDividerItemDecoration;
import it.integry.integrywmsnative.view.dialogs.input_lu_prod.DialogInputLUProdDTO;
import it.integry.integrywmsnative.view.dialogs.input_lu_prod.DialogInputLUProdView;
import it.integry.integrywmsnative.view.dialogs.printSsccUl.DialogPrintUlSSCCView;

/**
 * A simple {@link Fragment} subclass.
 * Use the {@link ProdRientroMerceOrderDetailFragment#newInstance} factory method to
 * create an instance of this fragment.
 */
public class ProdRientroMerceOrderDetailFragment extends BaseFragment implements ProdRientroMerceOrderDetailViewModel.Listener {


    public final MutableLiveData<OrdineLavorazioneDTO> currentOrder = new MutableLiveData<>();

    @Inject
    ArticoloRESTConsumer mArticoloRESTConsumer;

    @Inject
    SystemRESTConsumer mSystemRESTConsumer;

    @Inject
    ProdRientroMerceOrderDetailViewModel mViewModel;

    private FragmentProdRientroMerceOrderDetailBinding mBindings;
    private ProdRientroMerceOrderDetailMtbColtListAdapter mAdapter;

    private RunnableArgs<MtbColt> onMtbColtClicked;

    private BluetoothManager bluetoothManager;
    private BluetoothSerialDevice mConnectedBluetoothDevice;
    private String mLatestCodTcol;


    public ObservableField<BigDecimal> sumLUNumber = new ObservableField<>(BigDecimal.ZERO);
    public ObservableField<BigDecimal> sumColliNumber = new ObservableField<>(BigDecimal.ZERO);
    public ObservableField<BigDecimal> sumNetKG = new ObservableField<>(BigDecimal.ZERO);
    public ObservableField<BigDecimal> sumGrossKG = new ObservableField<>(BigDecimal.ZERO);
    public ObservableField<Integer> progress = new ObservableField<>(0);
    public boolean isOrderColli = false;

    public ProdRientroMerceOrderDetailFragment() {
        // Required empty public constructor
    }

    public static ProdRientroMerceOrderDetailFragment newInstance() {
        return new ProdRientroMerceOrderDetailFragment();
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        mBindings = DataBindingUtil.inflate(LayoutInflater.from(getActivity()), R.layout.fragment_prod_rientro_merce_order_detail, container, false);
        mBindings.setLifecycleOwner(this);

        mBindings.setView(this);

        MainApplication.appComponent
                .prodRientroMerceOrderDetailComponent()
                .create()
                .inject(this);

        mViewModel.setListener(this);

        if (currentOrder.getValue() != null) {
            refreshOrder();
            isOrderColli = currentOrder.getValue().getQtaCnf().compareTo(BigDecimal.ONE) == 0;
        }
        this.mViewModel.mtbColtsOfOrder.observe(getViewLifecycleOwner(), this::refreshList);

        this.initRecyclerView();
        this.initULScaleBluetoothConnection();

        mBindings.executePendingBindings();
        return mBindings.getRoot();
    }

    private void initRecyclerView() {
        mBindings.mainList.setNestedScrollingEnabled(false);
        mBindings.mainList.setHasFixedSize(true);
        mBindings.mainList.setLayoutManager(new LinearLayoutManager(requireActivity()));

        SimpleDividerItemDecoration itemDecorator = new SimpleDividerItemDecoration(requireActivity(), SimpleDividerItemDecoration.VERTICAL);
        itemDecorator.setDrawable(ContextCompat.getDrawable(requireActivity(), R.drawable.divider));
        mBindings.mainList.addItemDecoration(itemDecorator);

        mAdapter = new ProdRientroMerceOrderDetailMtbColtListAdapter(new ObservableArrayList<>());
        mAdapter.setEmptyView(mBindings.mtbColtEmptyView);

        mBindings.mainList.setAdapter(mAdapter);
        mAdapter.setOnItemClickListener(item -> {
            if (this.onMtbColtClicked != null) this.onMtbColtClicked.run(item);
        });
    }

    private void initULScaleBluetoothConnection() {
        bluetoothManager = BluetoothManager.getInstance();
        if (bluetoothManager == null) {
            // Bluetooth unavailable on this device :( tell the user
            Toast.makeText(requireActivity(), "Bluetooth not available.", Toast.LENGTH_LONG).show(); // Replace context with your context instance.

            return;
        }

        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext());
        var btMacAddress = sharedPreferences.getString(MainSettingsFragment.KEY_PALLET_BT_DEVICE, null);

        if (btMacAddress == null) {
            Toast.makeText(requireActivity(), "Nessun dispositivo BLUETOOTH configurato nelle impostazioni.", Toast.LENGTH_LONG).show(); // Replace context with your context instance.
            return;
        }

        var btName = Stream.of(bluetoothManager.getPairedDevicesList())
                .filter(x -> x.getAddress().equalsIgnoreCase(btMacAddress))
                .map(BluetoothDevice::getName)
                .findFirstOrElse(null);

        var disposableBluetooth = bluetoothManager.openSerialDevice(btMacAddress)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(this::onConnected, x -> this.onError(new BluetoothConnectionException(btName, btMacAddress, x)));

    }

    private void onConnected(BluetoothSerialDevice connectedDevice) {
        Toast.makeText(requireActivity(), "Connesso a: " + connectedDevice.getMac(), Toast.LENGTH_LONG).show();
        mConnectedBluetoothDevice = connectedDevice;
    }

    @Override
    public void onDetach() {
        mLatestCodTcol = null;
        if (mConnectedBluetoothDevice != null)
            bluetoothManager.close();

        super.onDetach();
    }

    public void setOrder(OrdineLavorazioneDTO order) {
        currentOrder.setValue(order);

        refreshOrder();
    }

    private void refreshOrder() {
        if (mBindings != null) mBindings.invalidateAll();
        if (mAdapter != null) mAdapter.clearDataset();
        if (mViewModel != null) mViewModel.setOrder(currentOrder.getValue());
    }

    private void refreshList(List<MtbColt> mtbColts) {
        if (mtbColts != null) {
            sumLUNumber.set(new BigDecimal(mtbColts.size()));

            AtomicBigDecimal sumColli = new AtomicBigDecimal(BigDecimal.ZERO);
            AtomicBigDecimal sumNet = new AtomicBigDecimal(BigDecimal.ZERO);
            AtomicBigDecimal sumGross = new AtomicBigDecimal(BigDecimal.ZERO);
            AtomicBigDecimal sumQtaCol = new AtomicBigDecimal(BigDecimal.ZERO);

            Stream.of(mtbColts)
                    .forEach(x -> Stream.of(x.getMtbColr()).forEach(y -> {
                        sumColli.getAndAdd(y.getNumCnf());
                        sumNet.getAndAdd(y.getPesoNettoKg());
                        sumGross.getAndAdd(y.getPesoLordoKg());
                        sumQtaCol.getAndAdd(y.getQtaCol());
                    }));

            ComparatorCompat<MtbColt> c = ComparatorCompat
                    .chain(
                            new ComparatorCompat<MtbColt>((x, y) -> x.getDataVersD().compareTo(y.getDataVersD()))
                    )
                    .reversed();
            MtbColt lastUl = Stream.of(mtbColts).sorted(c).findFirstOrElse(null);
            if (lastUl != null && !UtilityString.isNullOrEmpty(lastUl.getCodTcol())) {
                mLatestCodTcol = lastUl.getCodTcol();
            }

            currentOrder.getValue().setQtaTrasferite(sumQtaCol.get());
            this.progress.set(currentOrder.getValue().getProgress());
            sumColliNumber.set(sumColli.get());
            sumGrossKG.set(sumGross.get());
            progress.set(currentOrder.getValue().getProgress());
            sumNetKG.set(sumNet.get());
        } else {
            sumLUNumber.set(BigDecimal.ZERO);
            sumColliNumber.set(BigDecimal.ZERO);
            sumGrossKG.set(BigDecimal.ZERO);
            sumNetKG.set(BigDecimal.ZERO);
        }

        mAdapter.updateDataset(mtbColts);
    }

    @Override
    public void onDataSaved(MtbColt mtbColt) {
        String codJcom = currentOrder.getValue().getCodJcom();

        DialogPrintUlSSCCView.newInstance(codJcom, mtbColt, result -> {
                    if (result.isFlagPrintShipmentLabel() || result.isFlagPrintProductionLabel()) {
                        this.mViewModel.printSavedMtbColt(result);
                    } else {
                        this.mViewModel.refreshMtbColts();
                    }
                })
                .show(requireActivity().getSupportFragmentManager(), "DialogPrintUlSSCC");

    }


    public void addULButtonClick() {
        this.onLoadingStarted();

        OrdineLavorazioneDTO currentOrder = this.currentOrder.getValue();
        if (currentOrder == null) {
            this.onError(new Exception("Nessun ordine rilevato!"));
            return;
        }
        String codProd = currentOrder.getCodProd();
        mArticoloRESTConsumer.getByCodMart(codProd, mtbAart -> {

            String sql = "SELECT TOP 1 * " +
                    "FROM ( " +
                    "         SELECT MAX(id_step) OVER ( PARTITION BY gestione, num_ord, data_ord) as last_step, " +
                    "                * " +
                    "         FROM dtb_ord_steps " +
                    "         WHERE cod_jfas IS NOT NULL " +
                    "           AND gestione = " + UtilityDB.valueToString(currentOrder.getGestione()) +
                    "           AND num_ord = " + UtilityDB.valueToString(currentOrder.getNumOrd()) +
                    "           AND data_ord = " + UtilityDB.valueToString(currentOrder.getDataOrdD()) +
                    "     ) tmp " +
                    "WHERE data_iniz IS NOT NULL " +
                    "  AND (data_fine IS NULL OR last_step = id_step) " +
                    "ORDER BY id_step DESC";

            mSystemRESTConsumer.<List<DtbOrdSteps>>processSql(sql, new TypeToken<ArrayList<DtbOrdSteps>>() {
            }.getType(), dtbOrdStep -> {

                this.onLoadingEnded();

                String codTcol = currentOrder.getCodTcolUl();
                if (UtilityString.isNullOrEmpty(codTcol)) {
                    if (!UtilityString.isNullOrEmpty(mLatestCodTcol)) {
                        codTcol = mLatestCodTcol;
                    } else {
                        codTcol = mtbAart.getCodTcolUl();
                    }
                }

                DialogInputLUProdDTO dialogInputLUProdDTO = new DialogInputLUProdDTO()
                        .setMtbAart(mtbAart)
                        .setNumCnf(currentOrder.getColliPedana())
                        .setQtaCnf(mtbAart.getQtaCnf())
                        .setQtaTot(currentOrder.getColliPedana().multiply(mtbAart.getQtaCnf()))
                        .setPartitaMag(currentOrder.getPartitaMag())
                        .setDataScad(currentOrder.getDataScadD())
                        .setCodJfasParent(currentOrder.getCodJfas())
                        .setCodTcol(codTcol)
                        .setSuggestedCodJfas(dtbOrdStep != null && dtbOrdStep.size() > 0 ? dtbOrdStep.get(0).getCodJfas() : null);


                DialogInputLUProdView
                        .newInstance(mConnectedBluetoothDevice != null ? mConnectedBluetoothDevice.toSimpleDeviceInterface() : null, dialogInputLUProdDTO, resultDTO -> {
                            ProdRientroMerceOrderDetailPickedQuantityDTO pickedQuantityDTO = new ProdRientroMerceOrderDetailPickedQuantityDTO()
                                    .setNumCnf(resultDTO.getNumCnf())
                                    .setPesoCollo(resultDTO.getPesoCollo())
                                    .setPesoNetto(resultDTO.getPesoNetto())
                                    .setTaraPed(resultDTO.getTaraPed())
                                    .setTaraCol(resultDTO.getTaraCol())
                                    .setPesoLordo(resultDTO.getPesoLordo())
                                    .setMtbTCol(resultDTO.getMtbTCol())
                                    .setJtbFasi(resultDTO.getJtbFasi());

                            this.mViewModel.createLU(pickedQuantityDTO);
                        }, () -> {
                            this.onLoadingEnded();
                        })
                        .show(requireActivity().getSupportFragmentManager(), "tag");

            }, this::onError);

        }, this::onError);
    }

    public ProdRientroMerceOrderDetailFragment setOnMtbColtClicked(RunnableArgs<MtbColt> onMtbColtClicked) {
        this.onMtbColtClicked = onMtbColtClicked;
        return this;
    }

    public void editMtbColr(MtbColr mtbColr) {

    }

    public void deleteMtbColr(MtbColr mtbColr) {
        MtbColt mtbColt = new MtbColt()
                .setGestione(mtbColr.getGestione())
                .setDataCollo(mtbColr.getDataColloS())
                .setSerCollo(mtbColr.getSerCollo())
                .setNumCollo(mtbColr.getNumCollo());

        this.mViewModel.deleteLU(mtbColt);
    }

    public Drawable getOrderStatusIcon() {
        if (currentOrder.getValue() != null) {
            switch (currentOrder.getValue().getStatoEnum()) {
                case IN_CORSO:
                    return ContextCompat.getDrawable(requireActivity(), R.drawable.ic_play_circle_filled_24dp);
                case PAUSA:
                    return ContextCompat.getDrawable(requireActivity(), R.drawable.ic_pause_circle_filled_24dp);
                case PROGRAMMATO:
                default:
                    return ContextCompat.getDrawable(requireActivity(), R.drawable.ic_clock_circle_outline_24dp);
            }
        } else
            return ContextCompat.getDrawable(requireActivity(), R.drawable.ic_clock_circle_outline_24dp);
    }

    public Integer getOrderStatusIconTintColor() {
        if (currentOrder.getValue() != null) {
            switch (currentOrder.getValue().getStatoEnum()) {
                case IN_CORSO:
                    return ContextCompat.getColor(requireActivity(), R.color.green_800);
                case PAUSA:
                    return ContextCompat.getColor(requireActivity(), R.color.yellow_800);
                case PROGRAMMATO:
                default:
                    return ContextCompat.getColor(requireActivity(), R.color.blue_800);
            }
        } else
            return ContextCompat.getColor(requireActivity(), R.color.blue_800);
    }

    public Drawable getOrderStatusLabelBackgroud() {
        if (currentOrder.getValue() != null) {
            switch (currentOrder.getValue().getStatoEnum()) {
                case IN_CORSO:
                    return ContextCompat.getDrawable(requireActivity(), R.drawable.label_success_text);
                case PAUSA:
                    return ContextCompat.getDrawable(requireActivity(), R.drawable.label_warning_text);
                case PROGRAMMATO:
                default:
                    return ContextCompat.getDrawable(requireActivity(), R.drawable.label_primary_text);
            }
        } else
            return ContextCompat.getDrawable(requireActivity(), R.drawable.label_primary_text);
    }

    public Integer getOrderStatusLabelTextColor() {
        if (currentOrder.getValue() != null) {
            switch (currentOrder.getValue().getStatoEnum()) {
                case IN_CORSO:
                    return ContextCompat.getColor(requireActivity(), R.color.green_800);
                case PAUSA:
                    return ContextCompat.getColor(requireActivity(), R.color.yellow_800);
                case PROGRAMMATO:
                default:
                    return ContextCompat.getColor(requireActivity(), R.color.blue_800);
            }
        } else
            return ContextCompat.getColor(requireActivity(), R.color.blue_800);
    }
}