package it.integry.integrywmsnative.gest.settings;

import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.os.Bundle;
import android.text.Html;
import android.text.SpannableString;
import android.util.Base64;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatTextView;
import androidx.preference.EditTextPreference;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import androidx.sqlite.db.SimpleSQLiteQuery;

import com.annimon.stream.Stream;
import com.harrysoft.androidbluetoothserial.BluetoothManager;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ExecutorService;

import javax.inject.Inject;

import it.integry.barcode_base_android_library.model.BarcodeSetting;
import it.integry.integrywmsnative.MainApplication;
import it.integry.integrywmsnative.R;
import it.integry.integrywmsnative.core.barcode_reader.BarcodeManager;
import it.integry.integrywmsnative.core.context.AppContext;
import it.integry.integrywmsnative.core.data_store.db.dao.RawDao;
import it.integry.integrywmsnative.core.interfaces.ITitledFragment;
import it.integry.integrywmsnative.core.rest.consumers.SystemRESTConsumer;
import it.integry.integrywmsnative.core.rest.model.MailAttachmentDTO;
import it.integry.integrywmsnative.core.rest.model.MailRequestDTO;
import it.integry.integrywmsnative.core.utility.UtilityResources;
import it.integry.integrywmsnative.view.dialogs.DialogProgressView;
import it.integry.integrywmsnative.view.dialogs.base.DialogSimpleMessageView;
import it.integry.integrywmsnative.view.dialogs.yes_no.DialogYesNoView;

public class MainSettingsFragment extends PreferenceFragmentCompat implements ITitledFragment, Preference.OnPreferenceChangeListener {

    @Inject
    public AppContext appContext;

    @Inject
    public RawDao rawDao;

    @Inject
    public SystemRESTConsumer systemRESTConsumer;

    @Inject
    public DialogProgressView mCurrentProgress;

    @Inject
    public ExecutorService executorService;


    private boolean progressOpened;

    private final ArrayList<Runnable> onPreDestroyList = new ArrayList<>();

    public static final String KEY_PALLET_BT_DEVICE = "pallet_scale_bt_device";
    public static final String KEY_TRIGGER_SCAN_MODE = "TRIG_SCAN_MODE";
    public static final String KEY_TRIGGER_SCAN_DELAY = "TRIG_SCAN_DELAY";
    public static final String KEY_BUTTON_EXPORT_LOG = "EXPORT_LOG";

    private Collection<BluetoothDevice> btPairedDevices;

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

        MainApplication
                .appComponent
                .mainSettingsFragmentComponent()
                .create()
                .inject(this);
    }

    @Override
    public void onCreateActionBar(AppCompatTextView titleText, Context context) {
        titleText.setText(context.getText(R.string.settings_category).toString());
    }

    @Override
    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
        setPreferencesFromResource(R.xml.app_preferences, rootKey);

        var palletScaleBtDeviceListPref = (ListPreference) findPreference(KEY_PALLET_BT_DEVICE);
        var triggerScanDelayPref = (EditTextPreference) findPreference(KEY_TRIGGER_SCAN_DELAY);
        var triggerScanModeListPref = (ListPreference) findPreference(KEY_TRIGGER_SCAN_MODE);
        setListPreferenceData(palletScaleBtDeviceListPref);
        palletScaleBtDeviceListPref.setOnPreferenceChangeListener(this);

        triggerScanDelayPref.setOnPreferenceChangeListener(this);
        triggerScanDelayPref.setSummary(triggerScanDelayPref.getText());

        loadScanModePreferences();
        triggerScanModeListPref.setOnPreferenceChangeListener(this);
        triggerScanModeListPref.setSummary(triggerScanModeListPref.getValue());

        Preference button = findPreference(KEY_BUTTON_EXPORT_LOG);
        button.setOnPreferenceClickListener(preference -> {
            exportLog();
            return true;
        });

    }


    protected void loadScanModePreferences() {
        var triggerScanModeListPref = (ListPreference) findPreference(KEY_TRIGGER_SCAN_MODE);
        if (triggerScanModeListPref != null) {
            var scanModes = BarcodeSetting.getTriggerScanModes();
            triggerScanModeListPref.setEntries(scanModes);
            triggerScanModeListPref.setEntryValues(scanModes);
            triggerScanModeListPref.setOnPreferenceChangeListener(this);
        }
    }

    protected void setListPreferenceData(ListPreference lp) {
        BluetoothManager 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;
        }

        btPairedDevices = bluetoothManager.getPairedDevicesList();

        if (!Stream.of(btPairedDevices).anyMatch(x -> x.getAddress().equalsIgnoreCase(lp.getValue()))) {
            lp.setValue(null);
        }

        updatePalletBtDeviceSummary(lp, lp.getValue());

        if (!btPairedDevices.isEmpty()) {
            CharSequence[] entries = new CharSequence[btPairedDevices.size()];
            Stream.of(btPairedDevices)
                    .map(BluetoothDevice::getName)
                    .toList()
                    .toArray(entries);

            CharSequence[] entryValues = new CharSequence[btPairedDevices.size()];
            Stream.of(btPairedDevices)
                    .map(BluetoothDevice::getAddress)
                    .toList()
                    .toArray(entryValues);

            lp.setEntries(entries);
            lp.setEntryValues(entryValues);
        } else {
            lp.setEntries(new CharSequence[0]);
            lp.setEntryValues(new CharSequence[0]);
            lp.setDialogMessage("Nessun dispositivo bluetooth associato. Associane uno dalle impostazioni bluetooth prima di procedere.");
        }
    }


    @Override
    public void addOnPreDestroy(Runnable onPreDestroy) {
        onPreDestroyList.add(onPreDestroy);
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();

        for (Runnable onPreDestroy : onPreDestroyList) {
            onPreDestroy.run();
        }
    }

    @Override
    public boolean onPreferenceChange(@NonNull Preference preference, Object newValue) {

        final int idx;
        switch (preference.getKey()) {
            case KEY_PALLET_BT_DEVICE:
                final var palletScaleBtDeviceListPref = (ListPreference) preference;
                updatePalletBtDeviceSummary(palletScaleBtDeviceListPref, (String) newValue);
                break;
            case KEY_TRIGGER_SCAN_DELAY:
                final var scanDelayPref = (EditTextPreference) preference;
                scanDelayPref.setSummary((String) newValue);
                updateBarcodeScannerSettings();
                break;
            case KEY_TRIGGER_SCAN_MODE:
                final var scanTriggerMode = (ListPreference) preference;
                scanTriggerMode.setSummary((String) newValue);
                updateBarcodeScannerSettings();
                break;

        }
        // Set result so knows to refresh itself
//        getActivity().setResult(RESULT_OK);
        return true;
    }

    private void updateBarcodeScannerSettings() {
        try {
            BarcodeManager.init(requireContext());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    private void updatePalletBtDeviceSummary(ListPreference listPref, String btDeviceAddress) {
        String btDeviceName = Stream.of(btPairedDevices)
                .filter(x -> x.getAddress().equalsIgnoreCase(btDeviceAddress))
                .map(BluetoothDevice::getName)
                .findFirstOrElse(UtilityResources.getString(R.string.pallet_scale_bt_device_selection_info));
        listPref.setSummary(btDeviceName);
    }


    private void exportLog() {
        DialogYesNoView.newInstance("Esportazione log", "Vuoi inviare il log degli eventi al supporto?", result -> {
                    this.openProgress();

                    executorService.execute(() -> {


                        File logFilePath = appContext.getLogFilePath();
                        var files = logFilePath.listFiles();

                        var fileToShare = Stream.of(files)
                                .sortBy(x -> -1 * x.lastModified())
                                .findFirstOrElse(null);
                        try {
                            List<MailAttachmentDTO> attachmentDTOList = new ArrayList<>();

                            if(fileToShare != null) {
                                var htmlContent = createAppLogAttachment(fileToShare);

                                byte[] buffer = htmlContent.getBytes();//specify the size to allow.
                                String base64 = Base64.encodeToString(buffer, Base64.NO_WRAP);

                                var logAttachment = new MailAttachmentDTO()
                                        .setFileName("wms_log.html")
                                        .setFileb64Content(base64);
                                attachmentDTOList.add(logAttachment);
                            }


                            rawDao.vacuumDb(new SimpleSQLiteQuery("pragma wal_checkpoint(full)"));
                            File[] dbFiles = new File[3];
                            dbFiles[0] = requireContext().getDatabasePath("integry_wms");
                            dbFiles[1] = requireContext().getDatabasePath("integry_wms-shm");
                            dbFiles[2] = requireContext().getDatabasePath("integry_wms-wal");

                            for(int i = 0; i < dbFiles.length; i++){
                                byte[] dbFileBytes = new byte[(int) dbFiles[i].length()];
                                FileInputStream inputStream = new FileInputStream(dbFiles[i]);
                                final int read = inputStream.read(dbFileBytes);
                                inputStream.close();

                                var dbAttachment = new MailAttachmentDTO()
                                        .setFileName(dbFiles[i].getName())
                                        .setFileb64Content(Base64.encodeToString(dbFileBytes, Base64.NO_WRAP));
                                attachmentDTOList.add(dbAttachment);
                            }

                            var mailRequest = new MailRequestDTO()
                                    .setTo("developer@integry.it")
                                    .setMsgText("Questo è una mail che contiene il log del WMS")
                                    .setSubject("U vuemmeess")
                                    .setAttachments(attachmentDTOList);


                            systemRESTConsumer.sendMail(mailRequest, this::closeProgress, ex -> {
                                this.closeProgress();
                                requireActivity().runOnUiThread(() -> {
                                    DialogSimpleMessageView
                                            .makeErrorDialog(new SpannableString(Html.fromHtml(ex.getMessage())), null, null)
                                            .show(requireActivity().getSupportFragmentManager(), "tag");
                                });
                            });
                        } catch (Exception ex) {
                            requireActivity().runOnUiThread(() -> {
                                DialogSimpleMessageView
                                        .makeErrorDialog(new SpannableString(Html.fromHtml(ex.getMessage())), null, null)
                                        .show(requireActivity().getSupportFragmentManager(), "tag");
                            });
                        }
                    });
                })
                .show(getParentFragmentManager(), "tag");
    }


    private void openProgress() {
        if (!progressOpened && !this.mCurrentProgress.isAdded()) {
            this.progressOpened = true;
            requireActivity().runOnUiThread(() -> {
                this.mCurrentProgress.show(requireActivity().getSupportFragmentManager(), "tag");
            });
        }
    }

    private void closeProgress() {
        if (progressOpened) {
            this.progressOpened = false;
            requireActivity().runOnUiThread(() -> {
                mCurrentProgress.dismiss();
            });
        }
    }


    private String createAppLogAttachment(File logFile) {
        //Read text from file
        StringBuilder text = new StringBuilder();

        try {
            BufferedReader br = new BufferedReader(new FileReader(logFile));
            String line;

            while ((line = br.readLine()) != null) {
                text.append(line);
                text.append('\n');
            }
            br.close();
        } catch (IOException e) {
            //You'll need to add proper error handling here
        }

        StringBuilder htmlContent = new StringBuilder("<html lang=\"en\" style=\"font-family: Helvetica;\">\n" +
                "<head>\n" +
                "    <title>WMS Log</title>\n" +
                "\n" +
                "    <style>\n" +
                "        .level-badge {\n" +
                "            border-radius: 2px;\n" +
                "            padding: 0.25em 0.5rem;\n" +
                "            text-transform: uppercase;\n" +
                "            font-weight: 770;\n" +
                "            font-size: 12px;\n" +
                "            letter-spacing: .3px;\n" +
                "        }\n" +
                "\n" +
                "        .level-badge.trace {\n" +
                "            background-color: #a6a6a6;\n" +
                "            color: #ffffff;\n" +
                "        }\n" +
                "\n" +
                "        .level-badge.debug {\n" +
                "            background-color: #b3e5fc;\n" +
                "            color: #23547b;\n" +
                "        }\n" +
                "\n" +
                "        .level-badge.info {\n" +
                "            background-color: #c8e6c9;\n" +
                "            color: #256029;\n" +
                "        }\n" +
                "\n" +
                "        .level-badge.warn {\n" +
                "            background: #feedaf;\n" +
                "            color: #8a5340;\n" +
                "        }\n" +
                "\n" +
                "        .level-badge.error, .level-badge.fatal {\n" +
                "            background-color: #ffcdd2;\n" +
                "            color: #c63737;\n" +
                "        }\n" +
                "\n" +
                "        .message {\n" +
                "            white-space: pre-wrap;\n" +
                "        }\n" +
                "    </style>\n" +
                "</head>\n" +
                "\n" +
                "<body>");

        String[] lines = text.toString().split("\n");

        for (String line : lines) {
            String datetime = line.substring(14, 37);
            String tmp = line.substring(38);
            String logLevel = tmp.substring(0, tmp.indexOf(","));
            tmp = tmp.substring(logLevel.length() + 1);
            String title = tmp.substring(0, tmp.indexOf(","));
            String message = tmp.substring(title.length() + 1);

            htmlContent.append("<span style=\"font-size: 0.7em;\">").append(datetime).append("</span><br />");
            htmlContent.append("<div><span class=\"level-badge ").append(logLevel.toLowerCase()).append("\">").append(logLevel).append("</span> <span>").append(title).append("</span></div>");
            htmlContent.append("<code class=\"message\">").append(message).append("</code>");
            htmlContent.append("<hr/>");
        }

        htmlContent.append("""

                <script type="text/javascript">
                    document.querySelectorAll(".message").forEach(el => el.innerHTML = el.innerHTML.replaceAll(/at it\\.integry.*?(?:<br>)/g, '<b>$&</b>'));
                </script>
                </body>
                </html>""");

        return htmlContent.toString();
    }
}
