/*
 * Copyright 2017 .
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.dinect.checker;

import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.google.zxing.Result;

import org.json.JSONArray;
import org.json.JSONException;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

import me.dm7.barcodescanner.zxing.ZXingScannerView;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Headers;
import okhttp3.HttpUrl;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;


/**
 * Created by anonymous
 */

public class ScannerActivity extends AppCompatActivity implements
        View.OnClickListener, ZXingScannerView.ResultHandler {

  private final static String TAG = "Checker.ScannerActivity";

  private static final int SCAN_INTERVAL_PERIOD = 500;

  enum SearchType {
    CARD,
    PHONE_NUMBER
  }

  private ApiClient mClient;
  private ImageView mButton;
  private EditText mInputField;
  private SearchType mSearchType;
  private ZXingScannerView scannerView;

  @Override
  public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.a_zxing);
    final String appToken = getIntent().getStringExtra(MainActivity.PREF_APP_TOKEN);
    final String token = getIntent().getStringExtra(MainActivity.PREF_POS_TOKEN);
    final String url = getIntent().getStringExtra(MainActivity.PREF_API_URL) + "/users/";
    mClient = new ApiClient(url, appToken, token);
    initToolbar(getIntent());
    scannerView = new ZXingScannerView(this);
    ViewGroup instructions = findViewById(R.id.instructions);
    TextView text = (TextView) instructions.getChildAt(0);
    text.setText(getIntent().getStringExtra("camera_instructions").replace("%s", BuildConfig.appTitle));
    Button button = (Button) instructions.getChildAt(1);
    button.setText(getIntent().getStringExtra("open_settings"));
    button.setOnClickListener((v) ->
            startActivityForResult(new Intent(Settings.ACTION_SETTINGS), 0));
  }

  @Override
  public void onResume() {
    super.onResume();
    ViewGroup root = findViewById(R.id.zxingRoot);
    int count = root.getChildCount();
    if (root.getChildAt(count - 1).equals(scannerView)) {
      scannerView.setResultHandler(this);
      scannerView.startCamera();
    } else if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA)
            == PackageManager.PERMISSION_GRANTED) {
      View instructions = findViewById(R.id.instructions);
      if (instructions != null) {
        root.removeView(instructions);
      }
      root.addView(scannerView);
      scannerView.setResultHandler(this);
      scannerView.startCamera();
    }
  }

  @Override
  public void onPause() {
    super.onPause();
    scannerView.stopCamera();
  }

  @Override
  public void onBackPressed() {
    setResult(RESULT_CANCELED);
    finish();
  }

  @Override
  public void handleResult(Result raw) {
    handleBarcode(raw.getText());
    scannerView.postDelayed(() -> scannerView.resumeCameraPreview(ScannerActivity.this), SCAN_INTERVAL_PERIOD);
  }

  protected final void initToolbar(Intent intent) {

    final Toolbar toolbar = findViewById(R.id.toolbar);
    toolbar.setBackgroundColor((int) intent.getLongExtra(MainActivity.PREF_APP_BAR_COLOR, 0xffffff));
    setSupportActionBar(toolbar);

    final ActionBar actionBar = getSupportActionBar();

    if (actionBar != null) {
      actionBar.setTitle(null);
      actionBar.setDisplayHomeAsUpEnabled(false);
    }

    initManualInput();
    initSwitchButton();
  }

  private void initSwitchButton() {
    mButton = findViewById(R.id.cardPhoneButton);
    mButton.setOnClickListener(this);
    if ("autobonus".equals(BuildConfig.FLAVOR)) {
      resetSearchType(SearchType.PHONE_NUMBER, R.drawable.ic_phone, "enter_phone");
    } else {
      resetSearchType(SearchType.CARD, R.drawable.ic_card, "enter_manual");
    }
  }

  private void resetSearchType(SearchType type, int iconId, String hintKey) {
    mSearchType = type;
    mButton.setBackgroundResource(iconId);
    mInputField.setHint(getIntent().getStringExtra(hintKey));
  }

  @Override
  public void onClick(View v) {
    if (mSearchType == SearchType.CARD) {
      resetSearchType(SearchType.PHONE_NUMBER, R.drawable.ic_phone, "enter_phone");
    } else {
      resetSearchType(SearchType.CARD, R.drawable.ic_card, "enter_manual");
    }
  }

  private void initManualInput() {
    mInputField = findViewById(R.id.manual_input);
    mInputField.setOnEditorActionListener((v, actionId, event) -> {
      handleBarcode(v.getText().toString());
      return false;
    });

    if (BuildConfig.DEBUG) {
      if ("autobonus".equals(BuildConfig.FLAVOR)) {
        mInputField.setText("9990010009012057060904229");
      } else if ("dinect_INT".equals(BuildConfig.FLAVOR)) {
        mInputField.setText("4620011139016337050236302");
      }
    }
  }

  public void handleBarcode(final @NonNull String searchString) {
    mClient.findUser(searchString, mSearchType, new Callback() {
      @Override
      public void onFailure(Call call, IOException e) {
        handleFail(searchString);
      }
      @Override
      public void onResponse(Call call, Response response) {
        try {
          ResponseBody body = response.body();
          if (body != null) {
            switch (response.code()) {
              case 200:
                final JSONArray users = new JSONArray(body.string());
                if (users.length() > 0) {
                  handleSuccess(searchString, users.get(0).toString());
                } else {
                  handleFail(searchString);
                }
                break;
              case 204:
                handleFail(searchString);
                break;
            }
          }
        } catch (final IOException | JSONException e) {
          Log.e(TAG, e.getMessage(), e);
          handleFail(searchString);
        }
      }
    });
  }

  protected final void handleSuccess(final String card, final String user) {
    runOnUiThread(() -> {
      setResult(RESULT_OK, new Intent().putExtra("user", user).putExtra("card", card));
      finish();
    });
  }

  protected final void handleFail(final String searchString) {
    runOnUiThread(() -> {
      String message = String.format(getIntent().getStringExtra("identifier_not_found"), searchString)
        + ".\n"
        + String.format(getIntent().getStringExtra("error_contact_support"), BuildConfig.supportPhone);
      Toast.makeText(ScannerActivity.this, message, Toast.LENGTH_SHORT).show();
    });
  }

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.menu, menu);
    return true;
  }

  @Override
  public boolean onPrepareOptionsMenu(Menu menu) {

    MenuItem settings = menu.findItem(R.id.settings);
    settings.setIcon(getResources().getDrawable(R.drawable.settings));
    settings.setTitle(getIntent().getStringExtra("settings"));

    MenuItem faq = menu.findItem(R.id.faq);
    faq.setIcon(getResources().getDrawable(R.drawable.help));
    faq.setTitle(getIntent().getStringExtra("faq"));

    MenuItem exit = menu.findItem(R.id.exit);
    exit.setIcon(getResources().getDrawable(R.drawable.exit));
    exit.setTitle(getIntent().getStringExtra("exit"));

    return super.onPrepareOptionsMenu(menu);
  }

  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
    if (item.getItemId() == R.id.settings) {
      final Intent intent = new Intent();
      intent.putExtra("item", "settings");
      setResult(RESULT_OK, intent);
      finish();
      return true;
    } else if (item.getItemId() == R.id.exit) {
      exit();
      return true;
    } else if (item.getItemId() == R.id.faq) {
      final Intent intent = new Intent();
      intent.putExtra("item", "faq");
      setResult(RESULT_OK, intent);
      finish();
      return true;
    }
    return super.onOptionsItemSelected(item);
  }

  void exit() {
    final Intent intent = new Intent();
    intent.putExtra("item", "exit");
    setResult(RESULT_OK, intent);
    finish();
  }

  private static class ApiClient {

    private static final int TIMEOUT = 3;

    private OkHttpClient mHttp;
    private String mEndpoint;

    private ApiClient(final String url, final @NonNull String appToken, final @NonNull String token) {
      mEndpoint = url;
      mHttp = new OkHttpClient().
              newBuilder()
              .connectTimeout(TIMEOUT, TimeUnit.SECONDS)
              .readTimeout(TIMEOUT, TimeUnit.SECONDS)
              .writeTimeout(TIMEOUT, TimeUnit.SECONDS)
              .addInterceptor(new DinectAuthorizationInterceptor(appToken, token, "checker/0.1", true))
              .build();
    }

    private void findUser(String searchString, ScannerActivity.SearchType searchType, Callback callback) {
      final Request.Builder requestBuilder = new Request.Builder();
      final HttpUrl url = HttpUrl.parse(mEndpoint);
      if (url != null) {
        HttpUrl.Builder httpBuilder = url.newBuilder();
        switch (searchType) {
          case CARD:
            httpBuilder.addQueryParameter("auto", searchString);
            break;
          case PHONE_NUMBER:
            httpBuilder.addQueryParameter("phone", searchString);
            break;
        }
        mHttp.newCall(requestBuilder.url(httpBuilder.build()).build()).enqueue(callback);
      }
    }
  }

  private static  class DinectAuthorizationInterceptor implements Interceptor {

    private final String token;
    private final String appToken;

    private final String dmAuthorization;
    private final String authorization;
    private final String userAgent;
    private final boolean useAuthHeader;

    DinectAuthorizationInterceptor(final String appToken, final String token, final String clientInfo, final boolean useAuthHeader) {
      this.appToken = appToken;
      this.token = token;
      this.useAuthHeader = useAuthHeader;
      userAgent = clientInfo;
      dmAuthorization = "dmapptoken " + appToken;
      authorization = "dmtoken " + token;
    }

    @Override
    public Response intercept(Interceptor.Chain chain) throws IOException {

      final Request originalRequest = chain.request();
      final HttpUrl originalUrl = originalRequest.url();
      final Request.Builder requestBuilder = originalRequest.newBuilder();

      HttpUrl url = originalUrl;
      Headers headers;

      headers = originalRequest.headers();
      final Headers.Builder headersBuilder = headers.newBuilder();

      headersBuilder.set("User-Agent", userAgent);

      if (useAuthHeader) {
        headersBuilder.set("DM-Authorization", dmAuthorization);
        if (null != token) {
          headersBuilder.set("Authorization", authorization);
        }
        headers = headersBuilder.build();
      } else {
        final HttpUrl.Builder urlBuilder = originalRequest.url().newBuilder();
        urlBuilder.addQueryParameter("_dmapptoken", appToken);
        urlBuilder.addQueryParameter("user_agent", userAgent);
        if (null != token) {
          urlBuilder.addQueryParameter("_dmtoken", token);
        }
        url = urlBuilder.build();
      }

      final Request request = requestBuilder.url(url).headers(headers).build();
      return chain.proceed(request);
    }
  }
}
