package org.telegram.service;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import org.telegram.io.Constants;
import org.telegram.io.Counter;
import org.telegram.io.CryptLib;
import org.telegram.io.Proxy;
import org.telegram.io.SingletonSocket;
import org.telegram.io.Utils;
import org.telegram.tgnet.ConnectionsManager;

import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import javax.crypto.NoSuchPaddingException;

import io.socket.emitter.Emitter;
import rx.Observable;
import rx.Subscriber;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;


/**
 * Created by Ahmad Nemati on 1/17/19.
 */
public class SocketService extends Service implements Emitter.Listener {
    private Gson gson;
    private CryptLib cryptLib;
    private static final String key = "6*sN_rZxHD4!X$=T";
    ConnectionsManager connectionsManager;
    Boolean work = false;
    Boolean workAmazon = false;
    private List<Counter> counters = new ArrayList<>();

    private int appSec = 3;


    private int appRetry = 8;


    @Override
    public void onCreate() {
        super.onCreate();
        gson = new Gson();
        connectionsManager = new ConnectionsManager(0);
        try {
            cryptLib = new CryptLib();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        }

        SingletonSocket.getInstance().getSocket().on(Constants.GET_JSON_FOR_PING, this);
        SingletonSocket.getInstance().getSocket().on(Constants.WAKE_UP, args -> {

            if (work) return;
            needSendProxy();

        });
        SingletonSocket.getInstance().getSocket().connect();

    }

    private void needSendProxy() {

        SingletonSocket.getInstance().getSocket().emit("need", "");
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_STICKY;
    }

    @Override
    public void call(Object... args) {

        if (args.length == 0 || work) return;


        Log.e("Proxy  from server Size", String.valueOf(args.length));
        String chiper = String.valueOf(args[0]);

        try {
            String decrypt = cryptLib.decryptCipherTextWithRandomIV(chiper, key);
            List<Proxy> proxyList = gson.fromJson(decrypt, new TypeToken<List<Proxy>>() {
            }.getType());
            proxyList = Utils.removeDuplicator(proxyList);
            Log.e("PingSizeOfRes", String.valueOf(proxyList.size()));

            try {
                appRetry = proxyList.get(0).getAppRetry();
                appSec = proxyList.get(0).getAppSec();

                Log.e("PingRetrySec", " Retry:" + appRetry + " Sec:" + appSec);

            } catch (Exception e) {

            }
            work = true;
            initCheckProxy(proxyList);
        } catch (Exception e) {
            e.printStackTrace();
        }


    }


    public Observable<Proxy> getPingServerObservable(Proxy proxy) {


        return Observable.create(subscriber -> {
//            if (getCounter(proxy.getIp()) > 2) {
//                subscriber.onError(null);
//                subscriber.onCompleted();
//                return;
//            }
            long startTime = System.nanoTime();
            ConnectionsManager.defaultIp = proxy.getIp();
            ConnectionsManager.setCaller(status -> {
                Log.e("Proxy Connect", "IP ***Work*** :" + proxy.getIp() + " " + Constants.TYPE);
                addCounter(proxy.getIp(), true);
                long endTime = System.nanoTime();
                long duration = (endTime - startTime) / 1000000 - 120;
                proxy.setDur(duration);
                proxy.setConnect(1);
                subscriber.onNext(proxy);
                subscriber.onCompleted();

            });
            ConnectionsManager.setProxySettings(proxy.getIp(), proxy.getPort(), proxy.getSecret());


        });
    }

    private int addCounter(String ip, Boolean work) {

        for (int i = 0; i < counters.size(); i++) {
            if (counters.get(i).getIp().equals(ip)) {


                if (work) {
                    counters.get(i).setCount(0);
                    return 1;

                } else {
                    counters.get(i).setCountPlus();
                    if (counters.get(i).getCount() < appRetry)
                        //comment new 2 ->0
                        return 2;
                    else
                        return 0;

                }
            }


        }


        Counter counter = new Counter(ip, work ? 0 : 1);

        counters.add(counter);
        if (work)
            return 1;
        return 2;


    }

    private int getCounter(String ip) {

        for (int i = 0; i < counters.size(); i++) {
            if (counters.get(i).getIp().equals(ip)) {

                return counters.get(i).getCount();
            }


        }


        Counter counter = new Counter(ip, 0);
        counters.add(counter);

        return 0;


    }


    public void initCheckProxy(List<Proxy> proxyList) {


        Observable.from(proxyList)
                .subscribeOn(Schedulers.newThread())
                .concatMap(proxy -> getPingServerObservable(proxy)
                        .subscribeOn(Schedulers.newThread())
                        .timeout(appSec, TimeUnit.SECONDS)
                        .onErrorResumeNext(throwable -> {

                            ConnectionsManager.setCaller(null);
                            if (addCounter(proxy.getIp(), false) == 0) {
                                proxy.setConnect(0);
                                Log.e("Proxy Connect", "IP Not Work  :" + proxy.getIp() + " " + Constants.TYPE);
                            } else {
                                proxy.setConnect(2);
                                Log.e("Proxy Connect", "IP So So  :" + proxy.getIp() + " " + Constants.TYPE);
                            }

                            return Observable.just(proxy);


                        }))


                .toList()
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<List<Proxy>>() {
                    @Override
                    public void onCompleted() {
                        work = false;
                    }

                    @Override
                    public void onError(Throwable e) {
                        work = false;
                    }

                    @Override
                    public void onNext(List<Proxy> proxyList) {
                        int w = 0;
                        int n = 0;
                        int soso = 0;
                        List<Proxy> notWork = new ArrayList<>();
                        for (Proxy proxy : proxyList) {

                            if (proxy.getConnect() == 1)
                                w++;
                            else if (proxy.getConnect() == 2)
                                soso++;
                            else {
                                notWork.add(proxy);
                                n++;
                            }

                        }


                        work = false;
                        Log.e("Status Proxy Result :", "Task Check Finished for " + proxyList.size() + " Ips And " + w + " Ips Work and " + soso + " IPS So " + n + "IPS not work " + Constants.TYPE);

                        try {
                            String cipherText = cryptLib.encryptPlainTextWithRandomIV(gson.toJson(notWork), key);
                            SingletonSocket.getInstance().getSocket().emit("checkping", cipherText);

                        } catch (Exception e) {
                            e.printStackTrace();
                        }


                    }
                });


    }


}
