package com.example.quiz;


import android.annotation.SuppressLint;
import android.app.Dialog;
import android.content.Intent;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.os.Bundle;

import androidx.activity.result.ActivityResultLauncher;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;

import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.GridLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.anggastudio.printama.Printama;
import com.example.quiz.data.model.Course;
import com.example.quiz.data.model.MiseInitiale;
import com.example.quiz.data.model.Pari;
import com.example.quiz.data.model.PariMise;
import com.example.quiz.data.model.ParisResponse;

import com.example.quiz.data.model.TypeOfBet;
import com.example.quiz.data.model.dtos.NotifPayload;
import com.example.quiz.data.remote.StompManager;
import com.example.quiz.databinding.FragmentBetValidationBinding;
import com.example.quiz.utils.BitMapUtils;
import com.example.quiz.utils.BluetoothUtils;
import com.example.quiz.utils.LoaderDialog;
import com.example.quiz.utils.MessageDialog;
import com.example.quiz.utils.MobiIotPrinterManager;
import com.example.quiz.utils.Result;
import com.example.quiz.utils.SharedPrefsHelper;
import com.example.quiz.utils.SunmiPrinterManager;
import com.example.quiz.viewModel.LogsViewModel;
import com.example.quiz.viewModel.PariMiseViewModel;
import com.example.quiz.viewModel.PariViewModel;
import com.example.quiz.viewModel.SharedViewModel;
import com.google.android.material.appbar.MaterialToolbar;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Type;

import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import java.util.Map;
import java.util.Objects;
import java.util.Observable;
import java.util.stream.Collectors;

import javax.inject.Inject;

import dagger.hilt.android.AndroidEntryPoint;

/**
 * A simple {@link Fragment} subclass.
 * Use the {@link BetValidation#newInstance} factory method to
 * create an instance of this fragment.
 */

@AndroidEntryPoint
public class BetValidation extends Fragment {

    FragmentBetValidationBinding binding;


    SharedViewModel shared;


    PariMiseViewModel pariMiseViewModel;

    List<MiseInitiale> misesInitiales;

    private AlertDialog dialog;


    Map<String, Integer> listProduits = Map.of("QUINTE", 5, "QUARTE", 4, "TIERCE", 3, "COUPLE_GAGNANT", 2, "COUPLE_PLACE", 2);

    @Inject
    StompManager stompManager;

    SunmiPrinterManager sunmiPrinterManager;

    private int nombreX;

    LogsViewModel logsViewModel;


    private boolean order;

    private long mise;

    String mobileName;

    ParisResponse pariNotPrint;



    private TypeOfBet typeOfBet;

     MutableLiveData<Boolean> isPrinterReady = new MutableLiveData<>(false);

    SharedPrefsHelper prefsHelper;


    private Course course;


    LoaderDialog loader;


    private int coeff;

    private ActivityResultLauncher<Intent> enableBluetoothLauncher;


    private final MutableLiveData<List<String>> selectedHorses = new MutableLiveData<>(List.of());

    PariViewModel pariViewModel;


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

    public static BetValidation newInstance() {
        BetValidation fragment = new BetValidation();
        Bundle args = new Bundle();
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        prefsHelper = SharedPrefsHelper.getInstance(getContext());
        mobileName = Build.MANUFACTURER;
        sunmiPrinterManager = SunmiPrinterManager.getInstance(requireContext());
        if(mobileName.toLowerCase().contains("sunmi")){
            sunmiPrinterManager.connectPrinter(status ->{
                isPrinterReady.setValue(status);
                sunmiPrinterManager.disableSystemMessages();
            });
        }else{
           MobiIotPrinterManager.getInstance().init(requireContext());
        }
    }


    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        binding = FragmentBetValidationBinding.inflate(inflater, container, false);
        loader = new LoaderDialog(getContext());
        logsViewModel = new ViewModelProvider(this).get(LogsViewModel.class);
        pariViewModel = new ViewModelProvider(this).get(PariViewModel.class);
        binding.coeff.setText(String.valueOf(1));
        coeff = Integer.parseInt(binding.coeff.getText().toString());
        binding.coeff.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

            }

            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                if (charSequence.toString().isEmpty()) {
                    binding.coeff.setError("Le coefficient est obligatoire");
                    return;
                }
                if (Integer.parseInt(charSequence.toString()) < 1) {
                    binding.coeff.setError("Le coefficient doit être supérieur ou égal à 1 ");
                    return;
                }
                coeff = Integer.parseInt(charSequence.toString());
                calculateMise(selectedHorses.getValue());

            }

            @Override
            public void afterTextChanged(Editable editable) {

            }
        });
        return binding.getRoot();
    }

    private void setupNumberGrid(GridLayout grid) {
        binding.gridNumbers.removeAllViews();
        int columns = 8;
        grid.setColumnCount(columns);
        if (shared.selectedCourse.getValue() != null) {
            for (int i = 1; i <= shared.selectedCourse.getValue().getNombrePartants(); i++) {
                createNumberItem(String.valueOf(i));
                grid.addView(createNumberItem(String.valueOf(i)));
            }
        }
        createNumberItem("X");
        grid.addView(createNumberItem("X"));
    }
    private TextView createNumberItem(String horse) {
        TextView textView = new TextView(requireContext());
        textView.setText(horse);
        textView.setTextColor(getResources().getColor(R.color.white));
        GridLayout.LayoutParams params = new GridLayout.LayoutParams();
        params.setMargins(1, 1, 1, 1);
        textView.setLayoutParams(params);
        textView.setTextSize(10);
        textView.setWidth(80);
        textView.setHeight(100);
        textView.setGravity(Gravity.CENTER);
        textView.setBackgroundResource(Objects.equals(horse, "X") ? R.drawable.x_background : R.drawable.number_background);
        if (!Objects.equals(horse, "X") && shared.selectedCourse.getValue().getNonPartants() != null && shared.selectedCourse.getValue().getNonPartants().contains(Integer.valueOf(horse))) {
            textView.setTextColor(getResources().getColor(R.color.white));
            textView.setBackgroundResource(R.drawable.number_background_grey);
        }
        textView.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
        textView.setOnClickListener(v -> {
            if (!Objects.equals(horse, "X") && shared.selectedCourse.getValue().getNonPartants() != null && shared.selectedCourse.getValue().getNonPartants().contains(Integer.valueOf(horse))) {
                Toast.makeText(getContext(), "Ce cheval n'est pas partant", Toast.LENGTH_SHORT).show();
                return;
            }
            final List<String> horses = new ArrayList<>(selectedHorses.getValue());
            if (horses.size() >= shared.typeOfBet.getValue().getNumberOfHorse() && horse.equals("X")) {
                Toast.makeText(getContext(), "Vous ne pouvez plus sélectionner X", Toast.LENGTH_SHORT).show();
                return;
            }
            if (_notClickable(horses) && horse.equals("X")) {
                Toast.makeText(getContext(), "Vous ne pouvez plus sélectionner X", Toast.LENGTH_SHORT).show();
                return;
            }
            if (horses.contains(horse) && !horse.equals("X")) {
                horses.remove(horse);
                selectedHorses.setValue(horses);
                v.setSelected(false);
                v.setBackgroundResource(R.drawable.number_background);
            } else {
                horses.add(horse);
                selectedHorses.setValue(horses);
                v.setSelected(true);
                v.setBackgroundResource(R.drawable.number_selected_background);
            }
            String combinationText = selectedHorses.getValue().stream()
                    .map(h -> h)
                    .collect(Collectors.joining("-"));
            binding.combination.setText(combinationText);
        });
        return textView;
    }


    boolean _notClickable(List<String> selectedHorses) {
        int numberOfElement = 0;
        if (shared.typeOfBet.getValue().getNumberOfHorse() < 2) {
            return true;
        }
        for (String horse : selectedHorses) {
            numberOfElement = horse.equals("X") ? numberOfElement + 1 : numberOfElement;
        }
        nombreX = numberOfElement;
        return numberOfElement == shared.typeOfBet.getValue().getNumberOfHorse() - 1;
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        if(prefsHelper!=null && prefsHelper.get("pariNoPrintId") != null && !prefsHelper.get("pariNoPrintId").isEmpty()){
            pariViewModel.getPariByNumero(prefsHelper.get("pariNoPrintId")).observe(getViewLifecycleOwner(), pariResult -> {
                switch (pariResult.status){
                    case LOADING: {
                        loader.show("Recuperation du pari non imprimé");
                        break;
                    }
                    case SUCCESS:{
                        loader.dismiss();
                        new android.app.AlertDialog.Builder(getContext())
                                .setTitle("Réimpréssion du pari "+ pariResult.data.getNumeroTicket())
                                .setMessage("Veuillez réimprimer le ticket SVP")
                                .setPositiveButton("OK", (dlg, which) -> {
                                    if(mobileName.toLowerCase().contains("sunmi")){
                                        if(isPrinterReady.getValue() != null && !isPrinterReady.getValue()){
                                            sunmiPrinterManager.connectPrinter(status ->{
                                                isPrinterReady.setValue(status);
                                            });
                                        }
                                        if(sunmiPrinterManager.printerStatus() != 1){
                                            MessageDialog.showError(getContext(), "Erreur lors de l'impréssion, Veuillez rééssayer S.V.P!");
                                            return;
                                        }
                                    }else{
                                        try {
                                            int mobiPrintStatus = MobiIotPrinterManager.getInstance().getPrinterStatus();
                                            if(mobiPrintStatus != 1){
                                                MessageDialog.showError(getContext(), "Erreur lors de l'impréssion, Veuillez rééssayer S.V.P!");
                                                return;
                                            }
                                        } catch (RemoteException e) {
                                            throw new RuntimeException(e);
                                        }
                                    }
                                    try {
                                        printPari(pariResult.data);
                                        _initializeToZero();
                                        dlg.dismiss();
                                        prefsHelper.save("pariNoPrintId", null);
                                    } catch (WriterException e) {
                                        throw new RuntimeException(e);
                                    } catch (FileNotFoundException e) {
                                        throw new RuntimeException(e);
                                    }
                                }).setCancelable(false).show();
                        break;
                    }
                    case ERROR:{
                        MessageDialog.showError(getContext(), "Erreur lors de la récupération!");
                        break;
                    }

                }
            });
        }
        super.onViewCreated(view, savedInstanceState);
        shared = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
        pariMiseViewModel = new ViewModelProvider(this).get(PariMiseViewModel.class);
        pariMiseViewModel.getBetInitMise().observe(
                getViewLifecycleOwner(),
                result -> {
                    switch (result.status) {
                        case LOADING: {
                            loader.show("Chargement des mise");
                            break;
                        }
                        case SUCCESS: {
                            misesInitiales = result.data;
                            loader.dismiss();
                            break;
                        }
                        case ERROR: {
                            loader.dismiss();
                            MessageDialog.showError(getContext(), result.message);
                        }
                        default:
                            break;
                    }
                }
        );
        if (shared.selectedCourse.getValue() != null) {
            stompManager.subscribe("courses/" + shared.selectedCourse.getValue().getId(), json -> {
                Type type = new TypeToken<NotifPayload<Course>>() {
                }.getType();
                NotifPayload<Course> notif = new Gson().fromJson(json, type);
                Course updatedCourse = notif.getPayload();
                if (shared.selectedCourse.getValue().getId() == updatedCourse.getId()) {
                    shared.setSelectedCourse(updatedCourse);
                    setupNumberGrid(binding.gridNumbers);
                }
            });
        }
        setSelectedTypeOfBetImage();
        pariViewModel = new ViewModelProvider(this).get(PariViewModel.class);
        typeOfBet = shared.typeOfBet.getValue();
        course = shared.selectedCourse.getValue();
        AppCompatActivity activity = (AppCompatActivity) getActivity();
        if (activity != null) {
            MaterialToolbar toolbar = activity.findViewById(R.id.toolbar);
            toolbar.setBackgroundColor(getResources().getColor(R.color.primary_green));
            activity.setSupportActionBar(toolbar);
            if (activity.getSupportActionBar() != null) {
                activity.getSupportActionBar().setTitle("Pari " + shared.selectedCourse.getValue().getNom());
            }
        }
        setupNumberGrid(binding.gridNumbers);
        binding.paymentType.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                String selected = binding.paymentType.getSelectedItem().toString();

                if (selected.equals("Orange Money")) {
                    binding.phoneNumber.setVisibility(View.VISIBLE);
                } else {
                    binding.phoneNumber.setVisibility(View.GONE); // ou GONE
                    binding.phoneNumber.setText(""); // vider le champ si non utilisé
                }
            }

            @Override
            public void onNothingSelected(AdapterView<?> parent) {
                binding.phoneNumber.setVisibility(View.INVISIBLE);
            }
        });

        selectedHorses.observe(getViewLifecycleOwner(), new Observer<List<String>>() {
            @Override
            public void onChanged(List<String> horses) {
                calculateMise(horses);
                if (shared.typeOfBet.getValue().getNumberOfHorse() > 2 && horses.size() >= shared.typeOfBet.getValue().getNumberOfHorse()) {
                    binding.order.setVisibility(View.VISIBLE);
                } else {
                    binding.order.setVisibility(View.GONE);
                }
                if (horses.contains("X")) {
                    if (selectedHorses.getValue().size() == shared.typeOfBet.getValue().getNumberOfHorse()) {
                        binding.elargie.setVisibility(View.VISIBLE);
                        binding.elargie.setText("Champ total");
                        return;
                    } else {
                        if (selectedHorses.getValue().size() > shared.typeOfBet.getValue().getNumberOfHorse()) {
                            binding.elargie.setVisibility(View.VISIBLE);
                            binding.elargie.setText("Champ partiel");
                            return;
                        }
                    }
                }
                if (horses.size() > shared.typeOfBet.getValue().getNumberOfHorse()) {
                    binding.elargie.setVisibility(View.VISIBLE);
                    binding.elargie.setText("Elargi");
                } else {
                    binding.elargie.setVisibility(View.GONE);
                }
            }
        });
        binding.order.setOnCheckedChangeListener((buttonView, isChecked) -> {
            order = isChecked;
            calculateMise(selectedHorses.getValue());
        });

        binding.betValidateBtn.setOnClickListener(v -> {
            if (binding.paymentType.getSelectedItem().toString().equals("Orange Money") && binding.phoneNumber.getText().toString().isEmpty()) {
                MessageDialog.showError(getContext(), "Veuillez saisir le numéro de téléphone");
                return;
            }

            if (shared.typeOfBet == null || shared.selectedCourse == null) {
                return;
            }


            int required = typeOfBet.getNumberOfHorse();
            if (Objects.requireNonNull(selectedHorses.getValue()).size() < required) {
                Toast.makeText(getContext(), "Veuillez sélectionner au moins" + required + " chevaux", Toast.LENGTH_SHORT).show();
                return;
            }

            if (this.mise == 0) {
                Toast.makeText(getContext(), "Pari non valide", Toast.LENGTH_SHORT).show();
                return;
            }
            if(prefsHelper!=null && prefsHelper.get("pariNoPrintId") != null && !prefsHelper.get("pariNoPrintId").isEmpty()){
                pariViewModel.getPariByNumero(prefsHelper.get("pariNoPrintId")).observe(getViewLifecycleOwner(), pariResult -> {
                    switch (pariResult.status){
                        case LOADING: {
                            loader.show("Recuperation du pari non imprimé");
                            break;
                        }
                        case SUCCESS:{
                            loader.dismiss();
                            new android.app.AlertDialog.Builder(getContext())
                                    .setTitle("Réimpréssion du pari "+ pariResult.data.getNumeroTicket())
                                    .setMessage("Veuillez réimprimer le ticket SVP")
                                    .setPositiveButton("OK", (dlg, which) -> {
                                        if(mobileName.toLowerCase().contains("sunmi")){
                                            if(isPrinterReady.getValue() != null && !isPrinterReady.getValue()){
                                                sunmiPrinterManager.connectPrinter(status ->{
                                                    isPrinterReady.setValue(status);
                                                    });
                                                    if(sunmiPrinterManager.printerStatus() != 1){
                                                        MessageDialog.showError(getContext(), "Erreur lors de l'impréssion, Veuillez rééssayer S.V.P!");
                                                        return;
                                                    }
                                                }
                                            }else{
                                                try {
                                                    int mobiPrinterStatus = MobiIotPrinterManager.getInstance().getPrinterStatus();
                                                    if(mobiPrinterStatus != 1){
                                                        MessageDialog.showError(getContext(), "Erreur lors de l'impréssion, Veuillez rééssayer S.V.P!");
                                                        return;
                                                    }
                                                } catch (RemoteException e) {
                                                    throw new RuntimeException(e);
                                                }
                                            }
                                        try {
                                            printPari(pariResult.data);
                                            _initializeToZero();
                                            dlg.dismiss();
                                            prefsHelper.save("pariNoPrintId", null);
                                        } catch (WriterException | FileNotFoundException e) {
                                            throw new RuntimeException(e);
                                        }
                                    }).setCancelable(false).show();
                            break;
                        }
                        case ERROR:{
                            MessageDialog.showError(getContext(), "Erreur lors de la récupération du pari!");
                            break;
                        }
                    }
                });
                return;
            }

            Pari pari = new Pari(
                    OffsetDateTime.now(ZoneOffset.UTC).toString(),
                    Long.valueOf(course.getId()),
                    Long.valueOf(prefsHelper.get("id")),
                    Long.valueOf(prefsHelper.get("terminalId")),
                    Long.valueOf(prefsHelper.get("pointDeVenteId")),
                    List.of(new PariMise(String.valueOf(shared.typeOfBet.getValue().getName()), mise)),
                    _getFormule(),
                    _getCombinaison(),
                    coeff,
                    "XOF",
                    "Pari"
            );
            if (dialog != null && dialog.isShowing()) {
                return;
            }
            _showPariDialog(pari);
        });

        binding.backBtn.setOnClickListener(v -> {
            getActivity().onBackPressed();
        });

        binding.deleteBtn.setOnClickListener(v -> {
            _initializeToZero();
        });
    }


    String _getCombinaison() {
        if (selectedHorses.getValue().contains("X") && shared.typeOfBet.getValue().getNumberOfHorse() < selectedHorses.getValue().size()) {
            String first = selectedHorses.getValue().subList(0, shared.typeOfBet.getValue().getNumberOfHorse()).stream()
                    .map(String::valueOf)
                    .collect(Collectors.joining(","));
            String last = selectedHorses.getValue().subList(shared.typeOfBet.getValue().getNumberOfHorse(), selectedHorses.getValue().size()).stream()
                    .map(String::valueOf)
                    .collect(Collectors.joining(","));
            return first + ",R," + last;
        } else {
            return selectedHorses.getValue().stream().map(String::valueOf).collect(Collectors.joining(","));
        }
    }

    List<String> _getFormule() {
        List<String> combinaison = selectedHorses.getValue();
        if (!combinaison.contains("X")) {
            if (!order) {
                return List.of("UNITAIRE");
            } else {
                return List.of("FORMULE_COMPLETE");
            }
        } else {
            if (shared.typeOfBet.getValue().getNumberOfHorse() < selectedHorses.getValue().size()) {
                if (order) {
                    return List.of("CHAMP_X", "FORMULE_COMPLETE");
                } else {
                    return List.of("CHAMP_X");
                }
            } else {
                if (order) {
                    return List.of("CHAMP_TOTAL", "FORMULE_COMPLETE");
                } else {
                    return List.of("CHAMP_TOTAL");
                }
            }
        }
    }

    private void setSelectedTypeOfBetImage() {
        switch (shared.typeOfBet.getValue().getName()) {
            case COUPLE_PLACE:
                binding.icTypeOfBet.setImageResource(R.drawable.ic_couple_place);
                break;
            case COUPLE_GAGNANT:
                binding.icTypeOfBet.setImageResource(R.drawable.ic_couple_gagnant);
                break;
            case TIERCE:
                binding.icTypeOfBet.setImageResource(R.drawable.ic_tierce);
                break;
            case QUARTE:
                binding.icTypeOfBet.setImageResource(R.drawable.ic_quarte);
                break;
            case QUINTE:
                binding.icTypeOfBet.setImageResource(R.drawable.ic_quinte_plus);
                break;
        }
    }

    public void printPari(ParisResponse pari) throws WriterException, FileNotFoundException {
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.pmu_logo);
        Bitmap barcode = BitMapUtils.generateBarcodeBitmap(pari.getNumeroTicket(), 384, 100);
        StringBuilder tspl = new StringBuilder();
        tspl.append("Bamako").append("\n");
        tspl.append(pari.getCourseNom()).append("\n");
        OffsetDateTime dateTime = OffsetDateTime.parse(pari.getHeureDepartPrevue());
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm");
        String formattedDate = dateTime.format(formatter);
        tspl.append(formattedDate).append("\n");
        tspl.append("Course ").append(String.valueOf(pari.getCourseId())).append("\n");
        tspl.append(sunmiPrinterManager.separationText()+ "\n");
        selectedHorses.setValue(Arrays.stream(pari.getCombinaison().split(",")).collect(Collectors.toList()));
        int requireHorseNumber = listProduits.get(pari.getTypesParisMises().get(0).getTypePari());
        boolean isElargie = selectedHorses.getValue().size() > requireHorseNumber;
        String typePari = pari.getTypesParisMises().get(0).getTypePari();
        tspl.append(isElargie ? typePari + "/Elargie" : typePari).append("\n");
        tspl.append(pari.getFormules().contains("FORMULE_COMPLETE") ?"COMBINAISON COMPLETE"+"\n":"");
        String combinationText = BitMapUtils.formatLineWithNumbers(selectedHorses.getValue().stream().map(String::valueOf).toArray(String[]::new), "-", requireHorseNumber);
        tspl.append(combinationText).append("\n");
        tspl.append("COEF:   ").append(String.valueOf(pari.getCoefficient()));
        tspl.append("\n").append(sunmiPrinterManager.separationText()).append("\n");
        tspl.append("MONTANT: ").append( BitMapUtils.formatMontant(pari.getTypesParisMises().get(0).getMiseTotale())).append(" XOF");
        tspl.append("\n").append(sunmiPrinterManager.separationText()).append("\n");
        tspl.append("AGENT: ").append(prefsHelper.get("code")).append("\n");
        tspl.append("DATE:  ").append(DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss").format(OffsetDateTime.parse(pari.getDateHeurePrise()))).append("\n");
        if(mobileName.toLowerCase().contains("sunmi")){
            String pariText = tspl.toString();
            sunmiPrinterManager.printPari(BitMapUtils.resizeToPrinterWidth(bitmap, 384), barcode, pari.getNumeroTicket(), pariText, new SunmiPrinterManager.PrintStatusListener() {
                @Override
                public void onStatusChanged(boolean status) {
                    if(!status){
                        prefsHelper.save("pariNoPrintId", String.valueOf(pari.getNumeroTicket()));
                        new Handler(Looper.getMainLooper()).post(new Runnable() {
                            @Override
                            public void run() {
                                new AlertDialog.Builder(getContext())
                                    .setTitle("Echec de l'impression")
                                    .setMessage("L'impréssion du ticket "+pari.getNumeroTicket()+" n'a pu aboutir, "+"\n"+"Veuillez vérifier le papier puis rééssayez!")
                                    .setPositiveButton("Ok", (dlg, which)->{
                                        if(sunmiPrinterManager.printerStatus() != 1){
                                            MessageDialog.showError(getContext(), "Erreur lors de l'impréssion, Veuillez rééssayer S.V.P!");
                                            return;
                                        }
                                        try {
                                            printPari(pari);
                                            dlg.dismiss();
                                            prefsHelper.save("pariNoPrintId", null);
                                        } catch (WriterException | FileNotFoundException e) {
                                            throw new RuntimeException(e);
                                        }
                                    })
                                    .setCancelable(false)
                                    .show();
                            }
                        });
                    }else{
                        prefsHelper.save("pariNoPrintId", null);
                        binding.mise.setText("0 CFA");
                    }
                }
            });
            return;
        }

//        MobiIotPrinterManager.getInstance().testImagePrinting(bitmap);

        MobiIotPrinterManager.getInstance().printPari(BitMapUtils.bitmapToBmp(BitMapUtils.resizeToPrinterWidth(bitmap, 384)), BitMapUtils.bitmapToBmp(BitMapUtils.resizeToPrinterWidth(barcode, 384)), pari.getNumeroTicket(), tspl, new MobiIotPrinterManager.MobiIotPrinterStatus() {
            @Override
            public void printStatusCode(int status) {
                Log.e("TAG", "### "+String.valueOf(status));
                if(status!=1){
                    prefsHelper.save("pariNoPrintId", String.valueOf(pari.getNumeroTicket()));
                    new Handler(Looper.getMainLooper()).post(new Runnable() {
                        @Override
                        public void run() {
                            new AlertDialog.Builder(getContext())
                                    .setTitle("Echec de l'impression")
                                    .setMessage("L'impréssion du ticket "+pari.getNumeroTicket()+" n'a pu aboutir, "+"\n"+"Veuillez vérifier le papier puis rééssayez!")
                                    .setPositiveButton("Ok", (dlg, which)->{
                                        try {
                                            int mobiPrinterStatus = MobiIotPrinterManager.getInstance().getPrinterStatus();
                                            if(mobiPrinterStatus != 1){
                                                MessageDialog.showError(getContext(), "Erreur lors de l'impréssion, Veuillez rééssayer S.V.P!");
                                                return;
                                            }
                                        } catch (RemoteException e) {
                                            throw new RuntimeException(e);
                                        }

                                        try {
                                            printPari(pari);
                                            dlg.dismiss();
                                            prefsHelper.save("pariNoPrintId", null);
                                        } catch (WriterException | FileNotFoundException e) {
                                            throw new RuntimeException(e);
                                        }
                                    })
                                    .setCancelable(false)
                                    .show();
                        }
                    });
                }else{
                    new Handler(Looper.getMainLooper()).post(new Runnable() {
                        @Override
                        public void run() {
                            prefsHelper.save("pariNoPrintId", null);
                            binding.mise.setText("0 CFA");
                        }
                    });
                }

            }
        });

//        try {
//            if (BluetoothUtils.needsBluetoothPermissions()) {
//                if (!BluetoothUtils.hasBluetoothPermission(requireContext())) {
//                    // Demande la permission si non accordée
//                    BluetoothUtils.requestBluetoothPermission(requireActivity());
//                    return; // arrête ici, la popup va apparaître
//                }
//            }
//
//            // 2️⃣ Permission OK, on peut afficher la liste
//
//            Printama.with(getContext()).printTextBuilder(tspl, bitmap, pari.getNumeroTicket(), barcode, new Printama.PrintCallback() {
//                @Override
//                public void onResult(boolean success, String errorMessage) {
//                    if (!success) {
//                        new android.app.AlertDialog.Builder(getContext())
//                                .setTitle("Impréssion pari")
//                                .setMessage("Voulez-vous rééimprimer ce ticket?")
//                                .setPositiveButton("Oui", (dialog, which) -> {
//                                    try {
//                                        printPari(pari);
//                                    } catch (WriterException e) {
//                                        throw new RuntimeException(e);
//                                    }
//                                })
//                                .setNegativeButton("Non", (dialog, which) -> {
//                                    dialog.dismiss();
//                                });
//                    } else {
//                        _initializeToZero();
//                    }
//                }
//            });
//        } catch (SecurityException e) {
//            Toast.makeText(requireContext(),
//                    "Permission Bluetooth non accordée", Toast.LENGTH_SHORT).show();
//        }
    }





    @SuppressLint({"MissingInflatedId", "SetTextI18n"})
    void _showPariDialog(Pari pari) {
        LayoutInflater inflater = getLayoutInflater();
        View view = inflater.inflate(R.layout.pari_confirmation, null);
        TextView numero_course = (TextView) view.findViewById(R.id.alert_course_numero);
        numero_course.setText("Numero Course: " + String.valueOf(shared.selectedCourse.getValue().getId()));
        TextView alert_pari_type = (TextView) view.findViewById(R.id.alert_pari_type);
        alert_pari_type.setText(String.valueOf(shared.typeOfBet.getValue().getName()));
        TextView is_elargie = (TextView) view.findViewById(R.id.alert_is_elargie);
        is_elargie.setText(selectedHorses.getValue().size() > shared.typeOfBet.getValue().getNumberOfHorse() ? "CE" : "SI");
        TextView alert_combinaison = (TextView) view.findViewById(R.id.alert_combinaison);
        alert_combinaison.setText(selectedHorses.getValue().stream()
                .map(String::valueOf)
                .collect(Collectors.joining("-")));
        TextView aler_coeff = (TextView) view.findViewById(R.id.alert_coeff);
        aler_coeff.setText("Coef:" + String.valueOf(coeff));
        TextView alert_montant = (TextView) view.findViewById(R.id.alert_montant);
        alert_montant.setText("Montant: " + String.valueOf(pari.getTypesParisMises().get(0).getMiseTotale()) + " CFA");
        AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
        builder.setView(view);
        builder.setCancelable(false);
        dialog = builder.create();
        dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));

        view.findViewById(R.id.alert_validate).setOnClickListener(v -> {
            if(mobileName.toLowerCase().contains("sunmi")){
                if(isPrinterReady.getValue() != null && !isPrinterReady.getValue()){
                    sunmiPrinterManager.connectPrinter(status ->{
                        isPrinterReady.setValue(status);
                    });
                }
                switch (sunmiPrinterManager.printerStatus()){
                    case 2:{
                        MessageDialog.showError(getContext(), "L'imprimante n'est pas connectée!");
                        return;
                    }
                    case 3:{
                        MessageDialog.showError(getContext(), "L'imprimante n'est pas disponible!");
                        return;
                    }
                    case 4:{
                        MessageDialog.showError(getContext(), "Veuillez insérer du papier dans l'imprimante, SVP!");
                        return;
                    }
                    case 5: {
                        MessageDialog.showError(getContext(), "Suchauffe iminante de l'imprimante, Veuillez laisser reposer!");
                        return;
                    }
                    case 6: {
                        MessageDialog.showError(getContext(), "Le capot est ouvert, veuillez fermer SVP!");
                        return;
                    }

                }
            }else{
                try {
                    int printerStatus = MobiIotPrinterManager.getInstance().getPrinterStatus();
                    if(printerStatus != 1){
                        MessageDialog.showError(getContext(), "Erreur lors de l'impréssion."+"\n"+" Veuillez vérifier le papier puis rééssayer!");
                        return;
                    }
                } catch (RemoteException e) {
                    throw new RuntimeException(e);
                }
            }
            pariViewModel.createPari(pari).observe(getViewLifecycleOwner(), new Observer<Result<ParisResponse>>() {
                @Override
                public void onChanged(Result<ParisResponse> pariResult) {
                    switch (pariResult.status) {
                        case ERROR:
                            loader.dismiss();
                            dialog.dismiss();
                            MessageDialog.showError(getContext(), pariResult.message);
                            view.findViewById(R.id.alert_validate).setEnabled(false);
                            break;
                        case LOADING:
                            loader.show("Prise de pari");
                            view.findViewById(R.id.alert_validate).setEnabled(false);
                            break;
                        case SUCCESS:
                            try {
                                loader.dismiss();
                                logsViewModel.insertLog(prefsHelper.get("id"), "BET", "Création du pari " + pariResult.data.getNumeroTicket() + ", type de paris: " + pariResult.data.getTypesParisMises().get(0).getTypePari() + ", combinaison:" + selectedHorses.getValue().stream().map(String::valueOf).collect(Collectors.joining("-")), System.currentTimeMillis());
                                _initializeToZero();
                                printPari(pariResult.data);
                                dialog.dismiss();
                                MessageDialog.showSuccess(getContext(), "Pari créé avec succès");
                            } catch (WriterException e) {
                                loader.dismiss();
                                MessageDialog.showError(getContext(), e.getMessage());
                                throw new RuntimeException(e);
                            } catch (FileNotFoundException e) {
                                throw new RuntimeException(e);
                            }
                            break;
                    }
                }
            });
        });
        view.findViewById(R.id.alert_cancel).setOnClickListener(v -> {
            dialog.dismiss();
        });
        dialog.show();
    }

    void _initializeToZero() {
        selectedHorses.postValue(new ArrayList<>());
        binding.combination.setText("");
        binding.order.setChecked(false);
        order = false;
        binding.order.setVisibility(View.GONE);
        binding.coeff.setText("1");
        coeff = Integer.parseInt(binding.coeff.getText().toString());
        setupNumberGrid(binding.gridNumbers);
    }

    public void calculateMise(List<String> selectedHorses) {
        _notClickable(selectedHorses);
        int nombreChevauxSelectionnes = selectedHorses.size();
        long mise = 0;
        int nonPartants = 0;
        if (shared.typeOfBet.getValue() == null || shared.selectedCourse.getValue() == null) {
            return;
        }
        if (shared.selectedCourse.getValue().getNonPartants() != null) {
            nonPartants = shared.selectedCourse.getValue().getNonPartants().size();
        }
        int typeOfBetHorses = shared.typeOfBet.getValue().getNumberOfHorse();
        int partants = shared.selectedCourse.getValue().getNombrePartants();
        Course.TypeParis courseName = shared.typeOfBet.getValue().getName();
        if (shared.typeOfBet.getValue().getNumberOfHorse() > nombreChevauxSelectionnes) {
            binding.mise.setText(mise + " CFA");
            return;
        }
        MiseInitiale miseModel = misesInitiales.stream()
                .filter(miseInitiale -> miseInitiale.getTypePari().equals(courseName))
                .findFirst()
                .orElse(null);
        if (miseModel == null) {
            MessageDialog.showError(getContext(), "Erreur lors de l'initialisation de la mise");
            _initializeToZero();
            return;
        }
        mise = miseModel.getMiseInitialeMin();

        Course.TypeParis typePari = shared.typeOfBet.getValue().getName();
        boolean estCoupleGagnantOuPlace = typePari.equals(Course.TypeParis.COUPLE_GAGNANT) || typePari.equals(Course.TypeParis.COUPLE_PLACE);
        Log.d("TYPE_PARI", typePari.toString());

        if (estCoupleGagnantOuPlace) {
            // Pour COUPLE_GAGNANT ou COUPLE_PLACE: coefficient < 200
            if (coeff > 200) {
                // Erreur: coefficient trop élevé
                MessageDialog.showError(getContext(), "Le coefficient doit être au plus 200 pour " + typePari.toString());
                _initializeToZero();
                return;
            }
        } else {
            // Pour les autres types: coefficient <= 20
            if (coeff > 20) {
                // Erreur: coefficient trop élevé
                MessageDialog.showError(getContext(), "Le coefficient doit être inférieur ou égal à 20 pour " + typePari.toString());
                _initializeToZero();
                return;
            }
        }

        mise = mise * coeff;
        if (nombreX > 0) {
            if (nombreChevauxSelectionnes == typeOfBetHorses) {
                mise = mise * _calculateArrangement((partants - (typeOfBetHorses - nombreX) - nonPartants), nombreX);
            } else {
                if (nombreChevauxSelectionnes - typeOfBetHorses < nombreX || nombreChevauxSelectionnes - typeOfBetHorses == 1) {
                    mise = 0;
                    this.mise = mise;
                    binding.mise.setText(mise + " CFA");
                    return;
                }
                mise = mise * _calculateArrangement(nombreChevauxSelectionnes - typeOfBetHorses, nombreX);
            }
            if (order) {
                mise = mise * _calculateArrangement(typeOfBetHorses, (typeOfBetHorses - nombreX));
            }
            this.mise = mise;
            binding.mise.setText(mise + " CFA");
            return;
        }
        if (!order) {
            mise = mise * _calculateCombinaison(nombreChevauxSelectionnes, typeOfBetHorses);
        } else {
            mise = mise * _calculateArrangement(nombreChevauxSelectionnes, typeOfBetHorses);
        }
        this.mise = mise;
        binding.mise.setText(mise + " CFA");
    }

    Long _calculateArrangement(int n, int k) {
        return _calculateFactorial(n) / _calculateFactorial(n - k);
    }

    Long _calculateCombinaison(int n, int k) {
        return _calculateFactorial(n) / (_calculateFactorial(k) * _calculateFactorial(n - k));
    }

    Long _calculateFactorial(int n) {
        long f = 1;
        if (n == 0) {
            return f;
        }
        for (int i = 1; i <= n; i++) {
            f *= i;
        }
        return f;
    }


    public String getFilePath(Bitmap bitmap, String fileName) {
        // Utiliser cacheDir au lieu de filesDir (fichiers temporaires)
        File logo = new File(getContext().getCacheDir(), fileName);

        try (FileOutputStream fos = new FileOutputStream(logo)) {
            // JPEG au lieu de PNG, qualité 85%
            bitmap.compress(Bitmap.CompressFormat.JPEG, 85, fos);
            return logo.getAbsolutePath();
        } catch (IOException e) {
            return null;
        }
    }



    // Écrire un mot (16 bits) en little-endian

    // Écrire un double mot (32 bits) en little-endian


    public static String generate12Digits() {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 8; i++) {
            int digit = (int) (Math.random() * 10); // 0 à 9
            sb.append(digit);
        }
        return sb.toString();
    }


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

    @Override
    public void onDestroy() {
        super.onDestroy();
        stompManager.disconnect();
    }
}