package id.mdd.implementationpartnerconnect;

import android.Manifest;
import android.app.AlertDialog;
import android.content.AsyncQueryHandler;
import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.media.MediaPlayer;
import android.nfc.NfcAdapter;
import android.nfc.NfcManager;
import android.nfc.Tag;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Message;
import android.os.StrictMode;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import com.mdd.topupUnik.ubal.ApiUtils;
import com.mdd.topupUnik.ubal.InitReader;
import com.mdd.topupUnik.ubal.MainCardProcessor;
import com.mdd.topupUnik.ubal.ReversalObject;
import com.mdd.topupUnik.ubal.SuccessUpdateBalance;
import com.mdd.topupUnik.ubal.UpdateBalanceView;
import com.mdd.topupUnik.ubal.http.response.CardBalanceAPDUResponse;
import com.mdd.topupUnik.ubal.lib.CardInfo;
import com.mdd.topupUnik.ubal.lib.CardInfoCommand;
import com.mdd.topupUnik.ubal.lib.CommandResult;
import com.mdd.topupUnik.ubal.lib.FindCardResponse;
import com.medicom.organicdrv.OrganicDriver;

import java.io.IOException;
import java.net.InetAddress;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.util.Locale;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import id.emhusnan.mdd.service.MDDCommand;
import id.emhusnan.mdd.service.MDDService;
import id.emhusnan.mdd.service.MDDUpdateBalanceView;
import id.emhusnan.mdd.service.UpdateBalanceUiMessageFactory;
import id.emhusnan.mdd.service.UpdateBalanceUiThreadHandler;

import static com.mdd.topupUnik.ubal.CardValidationError.ERROR_CARD_NOT_SUPPORTED;
import static com.mdd.topupUnik.ubal.CardValidationError.ERROR_DIFFERENT_ISSUER_CARD;
import static com.medicom.organicdrv.utilsLib.ByteArrayToHexString;
import static id.emhusnan.mdd.service.MDDCommand.COMMAND_GET_BALANCE;
import static id.emhusnan.mdd.service.MDDCommand.COMMAND_UPDATE_BALANCE;

public class MainActivity extends AppCompatActivity implements MDDUpdateBalanceView, InitListener, NfcAdapter.ReaderCallback {
    @BindView(R.id.pgProcess)
    ProgressBar mProgress;
    @BindView(R.id.pbloading)
    ProgressBar pbloading;
    @BindView(R.id.txtStatus)
    TextView mOpsResult;
    @BindView(R.id.textCardBalance)
    TextView textBalance;
    @BindView(R.id.textCardBalanceNew)
    TextView textNewBalance;
    @BindView(R.id.textCardNumber)
    TextView textCardNumber;
    @BindView(R.id.txtAmount)
    TextView textAmount;
    @BindView(R.id.bgCard)
    LinearLayout bgCard;
    @BindView(R.id.logoCard)
    ImageView logoCard;
    @BindView(R.id.btnUpdateBalance)
    Button btnUpdateBalance;
    @BindView(R.id.btnGantiKartu)
    ImageButton btnGantiKartu;

    private long oldBalance;
    private boolean working = false;


    DecimalFormat formatter = (DecimalFormat) NumberFormat.getInstance(Locale.US);

    private int MY_PERMISSIONS_REQUEST_READ_CONTACTS = 1;
    private static final int STORAGE_PERMISSION_CODE = 101;
    private int COMMAND = MDDCommand.COMMAND_GET_BALANCE;
    private String TAG = "MDDService";
    boolean hasPendingOperation = false;
    @BindView(R.id.btnAktivasiAar)
    Button btnAktivasiAar;
    private MainCardProcessor cardProcessor;
    private InitReader reader;

    boolean iya = false;


    protected int getCommand() {
        return COMMAND;
    }

    @OnClick(R.id.btnGantiKartu)
    void gantiKartuClicked() {
        setCommand(MDDCommand.COMMAND_GET_BALANCE);
        Log.d("btnGantiKartu", "clicked");
        Toast.makeText(this, "Tempel kartu anda..", Toast.LENGTH_SHORT).show();
        refreshUi();
    }

    private void refreshUi() {
        runOnUiThread(() -> {
            findViewById(R.id.lyUpdateBalance).setVisibility(View.GONE);
            bgCard.getBackground().setColorFilter(Color.parseColor("#221f1f"), PorterDuff.Mode.SRC_ATOP);
            mProgress.setVisibility(View.INVISIBLE);
            oldBalance = 0;
            textBalance.setText("Rp " + formatter.format(0));
            textCardNumber.setText("0000 0000 0000 0000");

            mOpsResult.setTextColor(getColorRes(R.color.text_normal));
            mOpsResult.setText(getResources().getString(R.string.tap_card));
            setCommand(MDDCommand.COMMAND_UPDATE_BALANCE);

        });
        beep();
    }

    @OnClick(R.id.btnUpdateBalance)
    void updateBalance() {
        setCommand(MDDCommand.COMMAND_UPDATE_BALANCE);
        Log.d("btnUpdateBalance", "clicked");
        Toast.makeText(this, "Tempel kartu anda..", Toast.LENGTH_SHORT).show();
    }

    private void setCommand(int commandUpdateBalance) {
        COMMAND = commandUpdateBalance;
        btnUpdateBalance.setText(COMMAND == MDDCommand.COMMAND_UPDATE_BALANCE ? "Update Balance" : "Cek Balance");
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        validateHasPermissions();
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);

        if (isInternetAvailable()) {
            setUp();
            if (iya) {
                Log.d(TAG, "IS PAX");
                if (working) return;
                displayResult(R.color.text_normal, R.string.keep_tap);
                showProgressBar();
                findViewById(R.id.lyUpdateBalance).setVisibility(View.GONE);

            } else {
                NfcManager manager = (NfcManager) getSystemService(Context.NFC_SERVICE);
                NfcAdapter adapter = manager.getDefaultAdapter();
                Log.d(TAG, "adapter:" + adapter + "," + Build.MODEL + "," + Build.BRAND);

                if (adapter == null || !adapter.isEnabled()) {
                    displayResult(R.color.text_error, R.string.nfc_not_found);
                }
            }
        } else {
            Toast.makeText(MainActivity.this, getResources().getString(R.string.connection_error), Toast.LENGTH_SHORT).show();
            hideProgressBar();
            hideResult();
            showDialog();
        }

        Tag tagFromIntent = getIntent().getParcelableExtra(NfcAdapter.EXTRA_TAG);
        if (tagFromIntent != null) {
            onTagDiscovered(tagFromIntent);
        }

        String imei = Settings.Secure.getString(getContentResolver(), "android_id");
        Log.e("imei", imei);

    }


    private void hideResult() {
        mOpsResult.setVisibility(View.GONE);
    }

    private void showProgressBar() {
        mProgress.setVisibility(View.VISIBLE);
    }

    private void validateHasPermissions() {
        if (Build.VERSION.SDK_INT > 9) {
            StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
            StrictMode.setThreadPolicy(policy);
        }

        // permission READ_PHONE_STATE
        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
            Toast.makeText(this, "Please grant permission", Toast.LENGTH_SHORT).show();
            if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                    Manifest.permission.READ_PHONE_STATE)) {
            } else {
                ActivityCompat.requestPermissions(this,
                        new String[]{Manifest.permission.READ_PHONE_STATE,
                                Manifest.permission.INTERNET,
                                Manifest.permission.READ_EXTERNAL_STORAGE},
                        MY_PERMISSIONS_REQUEST_READ_CONTACTS);
            }

        } else {
            Log.d("Init", "--> Init");
            ((MyApp) getApplication()).initAar(this);
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        Log.e("requestPermissionResult", "reqCode "+requestCode +" permission "+permissions +" grantResults "+grantResults);
        ((MyApp) getApplication()).initAar(this);
    }

    @Override
    protected void onResume() {
        Log.e("onResume", "onresume");
        super.onResume();
        if (isInternetAvailable()) {
            setCommand(MDDCommand.COMMAND_UPDATE_BALANCE);
            NfcManager manager = (NfcManager) getSystemService(Context.NFC_SERVICE);
            NfcAdapter adapter = manager.getDefaultAdapter();
            final int READER_FLAGS = NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK;
            if (adapter != null && adapter.isEnabled()) {
                adapter.enableReaderMode(this, this, READER_FLAGS, null);
            }

        } else {
            Toast.makeText(MainActivity.this, getResources().getString(R.string.connection_error), Toast.LENGTH_SHORT).show();
            hideProgressBar();
            hideResult();
            showDialog();
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        NfcManager manager = (NfcManager) getSystemService(Context.NFC_SERVICE);
        NfcAdapter adapter = manager.getDefaultAdapter();
        if (adapter != null && adapter.isEnabled()) {
            adapter.disableReaderMode(this);
        }
    }

    private void displayResult(int p, int p2) {
        mOpsResult.setTextColor(getColorRes(p));
        mOpsResult.setText(p2);
    }

    private void setUp() {
        DecimalFormatSymbols symbols = formatter.getDecimalFormatSymbols();
        symbols.setGroupingSeparator('.');
        formatter.setDecimalFormatSymbols(symbols);
        TextView text = findViewById(R.id.version);
        try {
            PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
            String version = pInfo.versionName;
            text.setText("Version: " + version);
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        hideProgressBar();

    }

    protected MDDUpdateBalanceView getMDDView() {
        return this;
    }

    @Override
    public void onNFcCardNotSupported(int error_code) {
        hasPendingOperation = false;
        Toast.makeText(MainActivity.this, R.string.nfc_not_found, Toast.LENGTH_SHORT).show();
        NfcManager manager = (NfcManager) getSystemService(Context.NFC_SERVICE);
        NfcAdapter adapter = manager.getDefaultAdapter();
        Log.d(TAG, "adapter:" + adapter + "," + Build.MODEL + "," + Build.BRAND);

        if (adapter == null || !adapter.isEnabled()) {
            mOpsResult.setTextColor(getResources().getColor(R.color.text_error));
            mOpsResult.setText(R.string.nfc_not_found);
        }
        showProgressBar();
        displayResult(R.color.text_normal, R.string.nfc_not_found);
        findViewById(R.id.lyUpdateBalance).setVisibility(View.GONE);
    }

    @Override
    public void onNfcCardFound() {
        hasPendingOperation = true;
        Toast.makeText(MainActivity.this, "Mohon Tunggu...", Toast.LENGTH_LONG).show();
        showProgressBar();
        displayResult(R.color.text_normal, R.string.keep_tap);
        findViewById(R.id.lyUpdateBalance).setVisibility(View.GONE);
    }

    @Override
    public void onFinishOperation() {
        hasPendingOperation = false;
        Toast.makeText(MainActivity.this, "On Finishoperation", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onBeginOperation() {
        hasPendingOperation = true;
        Toast.makeText(MainActivity.this, "Begin Transaction", Toast.LENGTH_SHORT).show();
        Log.d(TAG, "onBeginOperation: ");
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        displayResult(R.color.text_normal, R.string.keep_tap);
        showProgressBar();
        findViewById(R.id.lyUpdateBalance).setVisibility(View.GONE);
    }


    @Override
    public void onBalanceInfoAvailable(CardInfo cardInfo) {
        hasPendingOperation = false;
        Log.d(TAG, "onBalanceInfoAvailable: " + cardInfo.getBalance());

        runOnUiThread(() -> {
            mProgress.setVisibility(View.VISIBLE);
            if (cardInfo.getBankType().equals("BNI")) {
                bgCard.getBackground().setColorFilter(Color.parseColor("#F6630E"), PorterDuff.Mode.SRC_ATOP);
                logoCard.setImageResource(R.drawable.tapcash_logo);
            } else {
                bgCard.getBackground().setColorFilter(Color.parseColor("#013C76"), PorterDuff.Mode.SRC_ATOP);
                logoCard.setImageResource(R.drawable.logo_emoney);

            }
            oldBalance = cardInfo.getBalance();
            textBalance.setText("Rp " + formatter.format(cardInfo.getBalance()));
            textCardNumber.setText(cardInfo.getCardNumber());

            mOpsResult.setTextColor(getColorRes(R.color.text_normal));

        });
        beep();
    }

    @Override
    public void onUpdateBalaceSuccess(SuccessUpdateBalance successUpdateBalance) {
        hasPendingOperation = false;
        Log.d(TAG, "onUpdateBalaceSuccess: " + successUpdateBalance.getBalance());
        Log.d(TAG, "onUpdateBalaceSuccess: " + successUpdateBalance.getMessage());
        String appletType = successUpdateBalance.getAppletType().toUpperCase();
        String message = successUpdateBalance.getMessage();
        if (!message.contains("Tidak ada Pending Balance")) {
            long amount = successUpdateBalance.getBalance() - oldBalance;
            findViewById(R.id.lyUpdateBalance).setVisibility(View.VISIBLE);
            textBalance.setText("Rp " + formatter.format(successUpdateBalance.getBalance()));
            textBalance.setText("Rp " + formatter.format(oldBalance));
            textAmount.setText("Rp " + formatter.format(amount));
            textNewBalance.setText("Rp " + formatter.format(successUpdateBalance.getBalance()));
            textCardNumber.setText(successUpdateBalance.getCardNumber());
            displayResult(R.color.text_success, R.string.update_success);
        } else {
            textBalance.setText("Rp " + formatter.format(successUpdateBalance.getBalance()));
            textCardNumber.setText(successUpdateBalance.getCardNumber());
            displayResult(R.color.text_success, R.string.no_pending_balance);
        }
        hideProgressBar();
        beep();
        oldBalance = successUpdateBalance.getBalance();
    }

    private void beep() {
        MediaPlayer mp = MediaPlayer.create(this, R.raw.beep);
        mp.start();
    }

    private void hideProgressBar() {
        mProgress.setVisibility(View.GONE);
    }

    @Override
    public void onUpdateBalanceError(String reason) {
        Log.e("onError", "errorDisiniAja");
        hasPendingOperation = false;
        mProgress.setVisibility(View.INVISIBLE);
        findViewById(R.id.lyUpdateBalance).setVisibility(View.GONE);
        Log.d(TAG, "onUpdateBalanceError: " + reason);
        Log.i("ErrorUB", reason);
        if (reason.equalsIgnoreCase("Timed out")) {
            displayResult(R.color.text_error, R.string.connection_error);
        } else if (reason.contains("Reversal Success")) {
            Log.d("onUpdateBalanceError", "contains reversal success");
            displayResult(R.color.text_error, R.string.reversal_success);
        } else {
            Log.e("onUpdateBalanceError", "else");
            displayResult(R.color.text_error, R.string.update_balance_failed);
        }

    }


    private int getColorRes(int res) {
        if (Build.VERSION.SDK_INT < 23) {
            return getResources().getColor(res);
        }
        return getColor(res);
    }


    public boolean isInternetAvailable() {
        try {
            InetAddress ipAddr = InetAddress.getByName("google.com");
            return !ipAddr.equals("");
        } catch (Exception e) {
            return false;
        }
    }

    private void showDialog() {
        AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
        alertDialogBuilder.setTitle("Butuh koneksi internet");
        alertDialogBuilder
                .setMessage("Koneksi internet diperlukan untuk update balance silahkan ulangi")
                .setIcon(R.mipmap.ic_launcher)
                .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {

                    }
                });
        AlertDialog alertDialog = alertDialogBuilder.create();
        alertDialog.show();

    }

    private void execute(Tag tag) {
        Log.d("Execute", "--> execute");
        if (reader != null){
            cardProcessor = new MainCardProcessor(MainActivity.this, reader, new UpdateBalanceView() {
                @Override
                public void onUpdateBalanceSuccess(SuccessUpdateBalance successUpdateBalance) {
                    runOnUiThread(() -> MainActivity.this.onUpdateBalaceSuccess(successUpdateBalance));
                }

                @Override
                public void onUpdateBalanceError(String reason) {
                    runOnUiThread(() -> MainActivity.this.onUpdateBalanceError(reason));
                }

                @Override
                public void onCardDetected(CardInfo cardInfo) {
                    runOnUiThread(() -> MainActivity.this.onBalanceInfoAvailable(cardInfo));
                }

                @Override
                public void onErrorNfcCardMsg(int errorCode) {
                    runOnUiThread(() -> MainActivity.this.onUpdateBalanceError(""));
                }
            }, ApiUtils.prod_environment);
            cardProcessor.lockIsoDepCard(tag);
            String cardUid = ByteArrayToHexString(tag.getId());
            Log.d("TagFromIntent", cardUid);
            boolean hasCardUid = !cardUid.isEmpty();
            if (hasCardUid) {
                Log.d(TAG, "---> Send to UI Card Found");
                runOnUiThread(this::onNfcCardFound);

                CardInfoCommand command = new CardInfoCommand(reader.myReader);
                Log.e("myReader", "Myreader : "+reader.myReader);

                final CommandResult<CardInfo> execute = command.execute(cardUid);
                CardInfo detectedCardInfo = execute.getResult();
                Log.d("executeSuccess", "executeIsSuccess = " + execute.isSuccess());
                if (execute.isSuccess()) {
                    if (detectedCardInfo.getBankType().toLowerCase().contains("dki")
                            || detectedCardInfo.getBankType().toLowerCase().contains("bca")){
                        runOnUiThread(() -> {
                            MainActivity.this.onBalanceInfoAvailable(detectedCardInfo);
                            mProgress.setVisibility(View.GONE);
                            mOpsResult.setText(R.string.tap_card);
                        });
                    }else {
                        cardProcessor.getBalance(detectedCardInfo);
                        cardProcessor.updateBalance(detectedCardInfo, 1);
                    }
                } else {
                    runOnUiThread(() -> {
                        Log.e("error", "Messagenya : "+execute.getMessage());
                        MainActivity.this.onUpdateBalanceError("Kartu tidak terdeteksi");
                        cardProcessor.unlockIsoDepCard();
                    });
                }
                cardProcessor.unlockIsoDepCard();

            } else {
                //wrong card type
                cardProcessor.unlockIsoDepCard();
            }
        }
    }

    @Override
    public void onInitReaderComplete(InitReader reader) {
        pbloading.setVisibility(View.GONE);
        this.reader = reader;
        cardProcessor = new MainCardProcessor(MainActivity.this, reader, new UpdateBalanceView() {
            @Override
            public void onUpdateBalanceSuccess(SuccessUpdateBalance successUpdateBalance) {
                runOnUiThread(() -> MainActivity.this.onUpdateBalaceSuccess(successUpdateBalance));
            }

            @Override
            public void onUpdateBalanceError(String reason) {
                runOnUiThread(() -> MainActivity.this.onUpdateBalanceError(reason));
            }

            @Override
            public void onCardDetected(CardInfo cardInfo) {
                runOnUiThread(() -> MainActivity.this.onBalanceInfoAvailable(cardInfo));
            }

            @Override
            public void onErrorNfcCardMsg(int errorCode) {
                runOnUiThread(() -> MainActivity.this.onUpdateBalanceError(""));
            }
        }, ApiUtils.prod_environment);
    }

    @Override
    public void onTagDiscovered(Tag tag) {
        execute(tag);

    }

}