/*
 * Copyright (C) 2013 The CyanogenMod Project
 * Copyright (C) 2017 The LineageOS Project
 *
 * 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.cyanogenmod.setupwizard.setup;

import android.accounts.AccountManager;
import android.accounts.AccountManagerCallback;
import android.accounts.AccountManagerFuture;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.app.Activity;
import android.app.ActivityOptions;
import android.app.Fragment;
import android.app.FragmentManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.service.persistentdata.PersistentDataBlockManager;
import android.util.Log;

import com.android.setupwizardlib.util.ResultCodes;
import com.cyanogenmod.setupwizard.R;
import com.cyanogenmod.setupwizard.SetupWizardApp;
import com.cyanogenmod.setupwizard.cmstats.SetupStats;
import com.cyanogenmod.setupwizard.ui.LoadingFragment;
import com.cyanogenmod.setupwizard.util.SetupWizardUtils;

import java.io.IOException;

public class GmsAccountPage extends SetupPage {

    public static final String TAG = "GmsAccountPage";

    public static final String ACTION_RESTORE = "com.google.android.setupwizard.RESTORE";
    public static final String ACTION_PROGRESS = "com.google.android.setupwizard.PROGRESS";
    public static final String ACTION_VENDOR_SETUP = "com.google.android.setupwizard.VENDOR_SETUP";
    public static final String RESTORE_ACTION_ID = "mfm_restore_start";
    public static final String RESTORE_CHECK_ID = "restore_check";
    public static final String FRAGMENT_START_RESTORE =
            "com.google.android.setupwizard.account.StartRestoreFragment";
    public static final String FRAGMENT_CHECK_RESTORE =
            "com.google.android.setupwizard.account.CheckRestoreTokenFragment";

    public static final String EXTRA_AUTH_ACCOUNT = "authAccount";
    public static final String EXTRA_RESTORE_ACCOUNT = "restoreAccount";
    public static final String EXTRA_RESTORE_TOKEN = "restoreToken";

    private static final String RESTORE_WIZARD_SCRIPT =
            "android.resource://com.google.android.setupwizard/xml/wizard_script";

    private Fragment mFragment;

    public GmsAccountPage(final Context context, SetupDataCallbacks callbacks) {
        super(context, callbacks);
    }

    @Override
    public Fragment getFragment(FragmentManager fragmentManager, int action) {
        mFragment = fragmentManager.findFragmentByTag(getKey());
        if (mFragment == null) {
            Bundle args = new Bundle();
            args.putString(Page.KEY_PAGE_ARGUMENT, getKey());
            args.putInt(Page.KEY_PAGE_ACTION, action);
            mFragment = new LoadingFragment();
            mFragment.setArguments(args);
        }
        return mFragment;
    }

    @Override
    public String getKey() {
        return TAG;
    }

    @Override
    public int getTitleResId() {
        return R.string.loading;
    }

    @Override
    public int getIconResId() {
        return -1;
    }

    @Override
    public int getNextButtonTitleResId() {
        return R.string.skip;
    }

    @Override
    public void doLoadAction(FragmentManager fragmentManager, int action) {
        if (action == Page.ACTION_PREVIOUS) {
            getCallbacks().onPreviousPage();
        } else {
            super.doLoadAction(fragmentManager, action);
            if (!SetupWizardUtils.isNetworkConnected(mContext) && !SetupWizardUtils.frpEnabled(mContext)) {
                if (SetupWizardApp.DEBUG) {
                    Log.d(TAG, "No network, no FRP enforcement, skip GMS account");
                }
                getCallbacks().onNextPage();
            } else if (!SetupWizardUtils.accountExists(mContext, SetupWizardApp.ACCOUNT_TYPE_GMS)) {
                launchGmsAccountSetup();
            } else {
                // This can happen if the user goes from setup -> restore, but chooses to set
                // their device up as "new". Thus we need to not re-prompt this entire flow,
                // skip ahead. CYNGNOS-2459
                if (SetupWizardApp.DEBUG) {
                    Log.d(TAG, "Google account already setup, skip gms setup");
                }
                setHidden(true);
                getCallbacks().onNextPage();
            }
        }
    }

    @Override
    public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
        if (SetupWizardApp.DEBUG) {
            Log.d(TAG, "Received activity result from requestCode " + requestCode
                    + " with a resultCode of " + resultCode);
            if (data != null) {
                Bundle extras = data.getExtras();
                Log.d(TAG, "Within the activity result there were extras:");
                for (String extra : extras.keySet()) {
                    Log.d(TAG, "The key " + extra + " has a value of " + extras.get(extra));
                }
            }
        }
        if (requestCode == SetupWizardApp.REQUEST_CODE_SETUP_GMS &&
                resultCode == ResultCodes.RESULT_SKIP) {
            launchGmsVendorSetup();
        } else if (requestCode == SetupWizardApp.REQUEST_CODE_SETUP_GMS && data != null) {
            if (SetupWizardUtils.isOwner() && resultCode == Activity.RESULT_OK) {

                // If we don't have a restore token and a restore account, then we need to
                // prompt with the restore picker from googles setup wizard so the user
                // can select what device they would like to restore from. Otherwise,
                // we're coming from a Tap&Go scenario and we should just restore.
                boolean restorePicker = !data.hasExtra(EXTRA_RESTORE_TOKEN)
                        && !data.hasExtra(EXTRA_RESTORE_ACCOUNT) &&
                        data.hasExtra(EXTRA_AUTH_ACCOUNT);

                SetupStats.addEvent(SetupStats.Categories.EXTERNAL_PAGE_LOAD,
                        SetupStats.Action.EXTERNAL_PAGE_RESULT,
                        SetupStats.Label.GMS_ACCOUNT, "success");
                launchGmsRestorePage(restorePicker);
            } else {
                handleResult(requestCode, resultCode);
            }
        } else {
            if (requestCode == SetupWizardApp.REQUEST_CODE_RESTORE_GMS) {
                setHidden(true);
            }
            handleResult(requestCode, resultCode);
        }
        return true;
    }

    @Override
    public boolean doNextAction() {
        final boolean canSkip = canSkip();
        // return true to force this page to handle the next action.
        return !canSkip;
    }

    @Override
    public void onFinishSetup() {

    }

    private void handleResult(int requestCode, int resultCode) {
        if (resultCode == Activity.RESULT_CANCELED) {
            SetupStats.addEvent(SetupStats.Categories.EXTERNAL_PAGE_LOAD,
                    SetupStats.Action.EXTERNAL_PAGE_RESULT,
                    requestCode == SetupWizardApp.REQUEST_CODE_SETUP_GMS ?
                            SetupStats.Label.GMS_ACCOUNT : SetupStats.Label.RESTORE, "canceled");
            getCallbacks().onPreviousPage();
        }  else {
            if (resultCode == Activity.RESULT_OK) {
                SetupStats.addEvent(SetupStats.Categories.EXTERNAL_PAGE_LOAD,
                        SetupStats.Action.EXTERNAL_PAGE_RESULT,
                        requestCode == SetupWizardApp.REQUEST_CODE_SETUP_GMS ?
                                SetupStats.Label.GMS_ACCOUNT : SetupStats.Label.RESTORE, "success");
                getCallbacks().onNextPage();
            } else {
                if (canSkip()) {
                    SetupStats.addEvent(SetupStats.Categories.EXTERNAL_PAGE_LOAD,
                            SetupStats.Action.EXTERNAL_PAGE_RESULT,
                            requestCode == SetupWizardApp.REQUEST_CODE_SETUP_GMS ?
                                    SetupStats.Label.GMS_ACCOUNT : SetupStats.Label.RESTORE, "skipped");
                    getCallbacks().onNextPage();
                } else {
                    getCallbacks().onPreviousPage();
                }
            }
            if (SetupWizardUtils.accountExists(mContext, SetupWizardApp.ACCOUNT_TYPE_GMS)) {
                setHidden(true);
            }
        }
    }

    private void launchGmsRestorePage(boolean restorePicker) {
        if (SetupWizardApp.DEBUG) {
            Log.d(TAG, "Launching gms restore page with restorePicker " + restorePicker);
        }
        try {
            // GMS can disable this after logging in sometimes
            if (SetupWizardUtils.enableGMSSetupWizard(mContext)) {
                Intent intent = new Intent(ACTION_PROGRESS);
                if (!restorePicker) {
                    intent.setAction(ACTION_PROGRESS);
                    intent.putExtra(SetupWizardApp.EXTRA_FRAGMENT, FRAGMENT_START_RESTORE);
                    intent.putExtra(SetupWizardApp.EXTRA_ACTION_ID, RESTORE_ACTION_ID);
                } else {
                    intent.setAction(ACTION_PROGRESS);
                    intent.putExtra(SetupWizardApp.EXTRA_ACTION_ID, RESTORE_CHECK_ID);
                    intent.putExtra(SetupWizardApp.EXTRA_FRAGMENT, FRAGMENT_CHECK_RESTORE);
                }
                intent.putExtra(SetupWizardApp.EXTRA_ALLOW_SKIP, true);
                intent.putExtra(SetupWizardApp.EXTRA_USE_IMMERSIVE, true);
                intent.putExtra(SetupWizardApp.EXTRA_FIRST_RUN, true);
                intent.putExtra(SetupWizardApp.EXTRA_THEME, SetupWizardApp.EXTRA_MATERIAL_LIGHT);
                // XXX: Fool G's setup wizard into thinking it is their setup wizard.
                // This is necessary to get the material theme on the restore page.
                intent.putExtra("scriptUri", RESTORE_WIZARD_SCRIPT);
                ActivityOptions options =
                        ActivityOptions.makeCustomAnimation(mContext,
                                android.R.anim.fade_in,
                                android.R.anim.fade_out);
                SetupStats.addEvent(SetupStats.Categories.EXTERNAL_PAGE_LOAD,
                        SetupStats.Action.EXTERNAL_PAGE_LAUNCH,
                        SetupStats.Label.PAGE, SetupStats.Label.RESTORE);
                mFragment.startActivityForResult(
                        intent,
                        SetupWizardApp.REQUEST_CODE_RESTORE_GMS, options.toBundle());
                return;
            }
        } catch (Exception e) {
            e.printStackTrace();
            // XXX: In open source, we don't know what gms version a user has.
            // Bail if the restore activity is not found.
            getCallbacks().onNextPage();
        }
    }

    private void launchGmsVendorSetup() {
        if (SetupWizardApp.DEBUG) {
            Log.d(TAG, "Launching gms vendor setup page");
        }
        try {
            Intent intent = new Intent(ACTION_VENDOR_SETUP);
            intent.setPackage(SetupWizardUtils.GOOGLE_SETUPWIZARD_PACKAGE);
            intent.putExtra(SetupWizardApp.EXTRA_ALLOW_SKIP, true);
            intent.putExtra(SetupWizardApp.EXTRA_USE_IMMERSIVE, true);
            intent.putExtra(SetupWizardApp.EXTRA_FIRST_RUN, true);
            intent.putExtra(SetupWizardApp.EXTRA_THEME, SetupWizardApp.EXTRA_MATERIAL_LIGHT);
            ActivityOptions options =
                    ActivityOptions.makeCustomAnimation(mContext,
                            android.R.anim.fade_in,
                            android.R.anim.fade_out);
            mFragment.startActivityForResult(
                    intent,
                    SetupWizardApp.REQUEST_CODE_VENDOR_SETUP_GMS, options.toBundle());
            return;
        } catch (Exception e) {
            // Move on if the vendor setup activity is not found.
            getCallbacks().onNextPage();
        }
    }

    public boolean canSkip() {
        final PersistentDataBlockManager pdbManager = (PersistentDataBlockManager)
                mContext.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
        return pdbManager == null
                || pdbManager.getDataBlockSize() == 0
                || pdbManager.getOemUnlockEnabled();
    }

    private void launchGmsAccountSetup() {
        if (SetupWizardApp.DEBUG) {
            Log.d(TAG, "Launching gms account page");
        }
        Bundle bundle = new Bundle();
        bundle.putBoolean(SetupWizardApp.EXTRA_FIRST_RUN, true);
        bundle.putBoolean(SetupWizardApp.EXTRA_ALLOW_SKIP, true);
        bundle.putBoolean(SetupWizardApp.EXTRA_USE_IMMERSIVE, true);
        bundle.putBoolean(SetupWizardApp.EXTRA_SUPRESS_D2D_SETUP, !mContext.getPackageManager()
                .hasSystemFeature(PackageManager.FEATURE_NFC));

        AccountManager
                .get(mContext).addAccount(SetupWizardApp.ACCOUNT_TYPE_GMS, null, null,
                bundle, null, new AccountManagerCallback<Bundle>() {
                    @Override
                    public void run(AccountManagerFuture<Bundle> future) {
                        boolean error = false;
                        try {
                            Bundle result = future.getResult();
                            Intent intent = result
                                    .getParcelable(AccountManager.KEY_INTENT);
                            ActivityOptions options =
                                    ActivityOptions.makeCustomAnimation(mContext,
                                            android.R.anim.fade_in,
                                            android.R.anim.fade_out);
                            SetupStats.addEvent(SetupStats.Categories.EXTERNAL_PAGE_LOAD,
                                    SetupStats.Action.EXTERNAL_PAGE_LAUNCH,
                                    SetupStats.Label.PAGE, SetupStats.Label.GMS_ACCOUNT);
                            mFragment.startActivityForResult(intent,
                                    SetupWizardApp.REQUEST_CODE_SETUP_GMS, options.toBundle());
                        } catch (OperationCanceledException e) {
                            error = true;
                        } catch (IOException e) {
                            error = true;
                        } catch (AuthenticatorException e) {
                            Log.e(TAG, "Error launching gms account", e);
                            error = true;
                        } finally {
                            if (error && getCallbacks().
                                    isCurrentPage(GmsAccountPage.this)) {
                                if (canSkip()) {
                                    getCallbacks().onNextPage();
                                } else {
                                    getCallbacks().onPreviousPage();
                                }
                            }
                        }
                    }
                }, null);
    }
}