package com.angryburg.uapp.fragments;

import android.annotation.SuppressLint;
import android.app.Fragment;
import android.content.Intent;
import android.graphics.Color;
import android.net.MailTo;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.CookieManager;
import android.webkit.WebResourceRequest;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import com.angryburg.uapp.R;
import com.angryburg.uapp.application.United;
import com.angryburg.uapp.utils.P;
import com.angryburg.uapp.utils.UnitedPropertiesIf;

/**
 * A web view containing fragment
 */

public class UnitedWebFragment extends Fragment {
    /**
     * URI start for accessing app/src/main/res/raw
     */
    public static final String RESOURCE_FOLDER = "file:///android_res/raw/";
    private static final String TAG = UnitedWebFragment.class.getSimpleName();
    /**
     * Url to load in the page on creation of view
     */
    public String starting_url = null;
    /**
     * Whether we've authenticated in this fragment yet or not
     * Unfortunately we can't just call United.authenticator.getCookie() and set that on the webview,
     * webviews are especially picky about having cookies set on them and it just won't work.
     * So what we do is POST to /mod IN THE WEB VIEW, and request a redirect to starting_url.
     * This holds a boolean on whether we've done that or not yet, so we only do it once and
     * not every time the user rotates the screen
     */
    boolean authenticated = false;
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // If we're being created for the first time, pull the URL from or arguments. Otherwise,
        // pull it from the saved instance state (in case we rotated, something)
        if (savedInstanceState != null && savedInstanceState.containsKey("URL")) {
            starting_url = savedInstanceState.getString("URL");
        } else {
            starting_url = getArguments().getString("URL");
        }
        if (savedInstanceState != null && savedInstanceState.containsKey("authenticated")) authenticated = savedInstanceState.getBoolean("authenticated");
    }
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        final View res = inflater.inflate(R.layout.main, container, false);
        CookieManager manager = CookieManager.getInstance();
        manager.setAcceptCookie(true);
        res.post(new Runnable() {
            @SuppressLint({"SetJavaScriptEnabled", "JavascriptInterface"})
            @Override
            public void run() {
                // Set up our web view with a unitedPropertiesIf and the right starting url
                WebView webview = res.findViewById(R.id.main_webkit);
                webview.setBackgroundColor(Color.rgb(0, 0, 0));
                webview.getSettings().setJavaScriptEnabled(true);
                webview.getSettings().setAllowFileAccess(true);
                //webview.getSettings().setDomStorageEnabled(true);
                //webview.getSettings().setAllowFileAccessFromFileURLs(true);
                //webview.getSettings().setAllowUniversalAccessFromFileURLs(true);
                webview.addJavascriptInterface(new UnitedPropertiesIf(getActivity()), "unitedPropertiesIf");
                UnitedWebFragmentWebViewClient client = new UnitedWebFragmentWebViewClient();
                webview.setWebViewClient(client);
                // If it's not safe to view this page, finish() the activity. `client` will open the url in the default web browser
                if (client.shouldOverrideUrlLoading(starting_url)) {
                    getActivity().finish();
                    return;
                }
                try {
                    // If we're logged in, and we're about to connect to the awoo endpoint, and we haven't authenticated in this fragment yet, then authenticate
                    if (P.getBool("logged_in") && new URL(starting_url).getAuthority().equals(new URL(P.get("awoo_endpoint")).getAuthority()) && !authenticated) {
                        authenticated = true;
                        String data = "username=" + URLEncoder.encode(United.authorizer.username, "UTF-8");
                        data += "&password=" + URLEncoder.encode(United.authorizer.password, "UTF-8");
                        data += "&redirect=" + URLEncoder.encode(starting_url, "UTF-8");
                        webview.postUrl(P.get("awoo_endpoint") + "/mod", data.getBytes());
                        return;
                    }
                } catch (Exception ignored) {
                    //
                }
                // If we don't need to authenticate or we've already authenticated, just load the url
                webview.loadUrl(starting_url);
            }
        });
        return res;
    }


    /**
     * save url to saved instance state so we can restore after rotation, etc
     * @param outState the state to be saved
     */
    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        if (getView() == null) return;
        outState.putString("URL", ((WebView) getView().findViewById(R.id.main_webkit)).getUrl());
        outState.putBoolean("authenticated", authenticated);
        if (getView() == null) return;
        WebView webview = getView().findViewById(R.id.main_webkit);
        webview.saveState(outState);
    }

    /**
     * Do not expose the injected javascript interface to unauthorized websites
     */
    private class UnitedWebFragmentWebViewClient extends WebViewClient {
        @Override public boolean shouldOverrideUrlLoading(WebView view, String url) {
            return shouldOverrideUrlLoading(url);
        }
        boolean shouldOverrideUrlLoading(String url) {
            Log.i(TAG, "URL: " + url);
            if (P.getBool("override_authorizer")) return false;
            if (url.startsWith(RESOURCE_FOLDER)) return false;
            if (url.startsWith("mailto:")) {
                MailTo mt = MailTo.parse(url);
                Intent i = newEmailIntent(mt.getTo(), mt.getSubject(), mt.getBody(), mt.getCc());
                UnitedWebFragment.this.getActivity().startActivity(i);
                return true;
            }
            try {
                String authority = new URL(url).getAuthority();
                Collection<String> allowed = new ArrayList<>();
                allowed.add(new URL(P.get("awoo_endpoint")).getAuthority());
                allowed.add(new URL("https://dangeru.us").getAuthority());
                allowed.add(new URL("https://boards.dangeru.us").getAuthority());
                allowed.add(new URL("http://augmented.dangeru.us").getAuthority());
                allowed.add(new URL("http://prefetcher.dangeru.us").getAuthority());
                allowed.add(new URL("http://kiramiki.dangeru.us").getAuthority());
                boolean allow = false;
                // boolean allow = allowed.stream().map((x) -> x.equalsIgnoreCase(authority)).filter(x -> x).findAny().orElse(false);
                for (String allowed_authority : allowed) {
                    if (allowed_authority.equalsIgnoreCase(authority)) {
                        allow = true;
                        break;
                    }
                }
                if (!allow) {
                    //GenericAlertDialogFragment.newInstance("Refusing page load for unsafe url " + url + " -- Not in list of allowed authorities " + allowed, getFragmentManager());
                    Log.w(TAG, "Refusing page load for unsafe url " + url + " -- Not in list of allowed authorities " + allowed);
                    // launch the url in the default web browser, and since we'll return true (we should override the url) it won't get opened in the web view
                    Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
                    getActivity().startActivity(i);
                }
                return !allow;
            } catch (Exception e) {
                Log.e(TAG, Log.getStackTraceString(e));
                return true;
            }
        }
        @Override public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                return shouldOverrideUrlLoading(view, request.getUrl().toString());
            }
            GenericAlertDialogFragment.newInstance("Something went wrong in UnitedWebFragmentWebViewClient, this method should only be called on API level 24+, but it was called on an API level below 21", getFragmentManager());
            return true;
        }
        @Override public void onPageFinished(WebView view, String url) {
            super.onPageFinished(view, url);
            try {
                if (getActivity() == null) return; // happens sometimes when the internet is disconnected
                getActivity().setTitle(view.getTitle());
            } catch (Exception e) {
                // the activity was finished before we could set it, happens sometimes with launching external urls.
                e.printStackTrace();
            }
        }
    }

    /**
     * Restore web view state
     * @param savedInstanceState state to be restored
     */
    @Override
    public void onViewStateRestored(Bundle savedInstanceState) {
        super.onViewStateRestored(savedInstanceState);
        if (getView() != null && savedInstanceState != null) {
            WebView webview = getView().findViewById(R.id.main_webkit);
            webview.restoreState(savedInstanceState);
        }
    }

    /**
     * Creates an intent for an email activity
     * @param address the email address to send to
     * @param subject the subject
     * @param body the body
     * @param cc any email addresses to cc
     * @return an intent that when started, prompts the user to send the email
     */
    private static Intent newEmailIntent(String address, String subject, String body, String cc) {
        Intent intent = new Intent(Intent.ACTION_SEND);
        intent.putExtra(Intent.EXTRA_EMAIL, new String[] { address });
        intent.putExtra(Intent.EXTRA_TEXT, body);
        intent.putExtra(Intent.EXTRA_SUBJECT, subject);
        intent.putExtra(Intent.EXTRA_CC, cc);
        intent.setType("message/rfc822");
        return intent;
    }
}