package li.doerf.hacked.services;

import android.app.IntentService;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;

import org.joda.time.DateTime;

import java.io.IOException;
import java.util.List;

import li.doerf.hacked.db.HackedSQLiteHelper;
import li.doerf.hacked.db.tables.Account;
import li.doerf.hacked.db.tables.Breach;
import li.doerf.hacked.remote.BreachedAccount;
import li.doerf.hacked.remote.HaveIBeenPwned;
import retrofit2.Call;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;


public class HaveIBeenPwnedCheckService extends IntentService {
    private final String LOGTAG = getClass().getSimpleName();

    public HaveIBeenPwnedCheckService() {
        super("HaveIBeenPwnedCheckService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        Log.d(LOGTAG, "onHandleIntent");
        doCheck();
    }

    private void doCheck() {
        Context context = getBaseContext();
        SQLiteDatabase db = HackedSQLiteHelper.getInstance(context).getReadableDatabase();

        Cursor c = Account.listAll(db);
        int waitForNextRequest = 1500;

        while( c.moveToNext()) {
            Account account = Account.create( db, c);
            Log.d(LOGTAG, "Checking for account: " + account.getName());

            Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl("https://haveibeenpwned.com/")
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();

            HaveIBeenPwned service = retrofit.create(HaveIBeenPwned.class);
            Call<List<BreachedAccount>> breachedAccountsList = service.listBreachedAccounts( account.getName());

            if ( waitForNextRequest > 0 ) {
                try {
                    Log.d(LOGTAG, "waiting for " + waitForNextRequest + " before next request");
                    Thread.sleep( waitForNextRequest);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            try {
                Response<List<BreachedAccount>> response = breachedAccountsList.execute();

                if ( response.isSuccessful()) {
                    for (BreachedAccount ba : response.body()) {
                        Breach existing = Breach.findByAccountAndName(db, account, ba.getName());

                        if ( existing != null ) {
                            Log.d(LOGTAG, "breach already existing: " + ba.getName());
                            // update account
                            account.setLastChecked(DateTime.now());
                            account.update(db);
                            continue;
                        }

                        Log.d(LOGTAG, "new breach: " + ba.getName());
                        Breach breach = Breach.create(
                                account,
                                ba.getName(),
                                ba.getTitle(),
                                ba.getDomain(),
                                DateTime.parse(ba.getBreachDate()),
                                DateTime.parse(ba.getAddedDate()),
                                ba.getPwnCount(),
                                ba.getDescription(),
                                ba.getDataClass(),
                                ba.getIsVerified(),
                                false
                        );
                        breach.insert(db);
                        Log.i(LOGTAG, "breach inserted into db");

                        // update account
                        account.setLastChecked(DateTime.now());
                        account.setHacked(true);
                        account.update(db);
                    }
                } else {
                    if ( response.code() == 404 ) {
                        Log.i(LOGTAG, "no breach found: " + account.getName());

                        // update account
                        account.setLastChecked(DateTime.now());
                        account.update(db);
                    } else {
                        Log.w(LOGTAG, "unexpected response code: " + response.code());
                    }
                }

//                Log.d(LOGTAG, response.headers().toString());
                String retryAfter = response.headers().get("Retry-After");
                if ( retryAfter != null ) {
                    waitForNextRequest = Integer.parseInt(retryAfter) * 1000;
                } else {
                    waitForNextRequest = 1500;
                }
            } catch (IOException e) {
                Log.e(LOGTAG, "caughtIOException while contacting www.haveibeenpwned.com", e);
            }
        }

        Log.i(LOGTAG, "finished checking");
    }
}

@@ -312,16 +312,41 @@
 il.List;
+%0Aimport java.util.Random;
 %0A%0Aimport
@@ -815,24 +815,65 @@
 impleName();
+%0A    private static long noReqBefore = 0;
 %0A%0A    public
@@ -1113,24 +1113,77 @@
 doCheck() %7B%0A
+        Log.d(LOGTAG, %22starting check for braches%22);%0A
         Cont
@@ -1349,47 +1349,8 @@
 db);
-%0A        int waitForNextRequest = 1500;
 %0A%0A  
@@ -1907,33 +1907,32 @@
 unt.getName());%0A
-%0A
             if (
@@ -1931,31 +1931,94 @@
     
-if ( waitForNextRequest
+long timeDelta = noReqBefore - System.currentTimeMillis();%0A%0A            if ( timeDelta
  %3E 0
@@ -2099,30 +2099,23 @@
 %22 + 
-waitForNextRequest
+timeDelta
  + %22
+ms
  bef
@@ -2168,34 +2168,25 @@
 .sleep( 
-waitForNextRequest
+timeDelta
 );%0A     
@@ -2255,26 +2255,90 @@
     
-e.printStackTrace(
+Log.e(LOGTAG, %22caught InterruptedException while waiting for next request slot%22, e
 );%0A 
@@ -4699,24 +4699,70 @@
 ry-After%22);%0A
+                Random random = new Random();%0A
             
@@ -4813,36 +4813,184 @@
         
-waitForNextR
+Log.d(LOGTAG, String.format(%22haveibeenpwned wants us to wait for %25ss until next r
 equest
- =
+%22, retryAfter));%0A                    noReqBefore = System.currentTimeMillis() + (
  Integer
@@ -5017,16 +5017,40 @@
 ) * 1000
+ ) + random.nextInt(100)
 ;%0A      
@@ -5092,33 +5092,81 @@
     
-waitForNextRequest = 1500
+noReqBefore = System.currentTimeMillis() + ( 1500 + random.nextInt(100) )
 ;%0A  
@@ -5343,33 +5343,33 @@
  %7D%0A%0A        Log.
-i
+d
 (LOGTAG, %22finish
@@ -5379,16 +5379,28 @@
 checking
+ for beaches
 %22);%0A    

optimize request timing