package com.android.settings.development;

import static com.android.settings.development.DevelopmentOptionsActivityRequestCodes.REQUEST_MOCK_LOCATION_APP;

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.app.Activity;
import android.app.AppOpsManager;
import android.app.AppOpsManager.OpEntry;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;

import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;

import com.android.settings.R;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.util.ReflectionHelpers;

import java.util.Collections;

@RunWith(RobolectricTestRunner.class)
public class MockLocationAppPreferenceControllerTest {

    @Mock
    private DevelopmentSettingsDashboardFragment mFragment;
    @Mock
    private AppOpsManager mAppOpsManager;
    @Mock
    private PackageManager mPackageManager;
    @Mock
    private Preference mPreference;
    @Mock
    private PreferenceScreen mScreen;

    @Mock
    private ApplicationInfo mApplicationInfo;

    private Context mContext;
    private MockLocationAppPreferenceController mController;

    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
        mContext = RuntimeEnvironment.application;
        mController = spy(new MockLocationAppPreferenceController(mContext, mFragment));
        ReflectionHelpers.setField(mController, "mAppsOpsManager", mAppOpsManager);
        ReflectionHelpers.setField(mController, "mPackageManager", mPackageManager);
        when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
        mController.displayPreference(mScreen);
    }

    @Test
    public void updateState_foobarAppSelected_shouldSetSummaryToFoobar() {
        final String appName = "foobar";

        final AppOpsManager.PackageOps packageOps =
                new AppOpsManager.PackageOps(appName, 0,
                        Collections.singletonList(createOpEntry(AppOpsManager.MODE_ALLOWED)));
        when(mAppOpsManager.getPackagesForOps(any(int[].class))).thenReturn(
                Collections.singletonList(packageOps));

        mController.updateState(mPreference);

        verify(mPreference).setSummary(mContext.getString(R.string.mock_location_app_set, appName));
    }

    @Test
    public void updateState_noAppSelected_shouldSetSummaryToDefault() {
        when(mAppOpsManager.getPackagesForOps(any(int[].class)))
                .thenReturn(Collections.emptyList());

        mController.updateState(mPreference);

        verify(mPreference).setSummary(mContext.getString(R.string.mock_location_app_not_set));
    }

    @Test
    public void onActivityResult_fooPrevAppBarNewApp_shouldRemoveFooAndSetBarAsMockLocationApp()
            throws PackageManager.NameNotFoundException {
        final String prevAppName = "foo";
        final String newAppName = "bar";
        final Intent intent = new Intent();
        intent.setAction(newAppName);
        final AppOpsManager.PackageOps packageOps = new AppOpsManager.PackageOps(prevAppName, 0,
                Collections.singletonList(createOpEntry(AppOpsManager.MODE_ALLOWED)));

        when(mAppOpsManager.getPackagesForOps(any(int[].class)))
                .thenReturn(Collections.singletonList(packageOps));
        when(mPackageManager.getApplicationInfo(anyString(),
                eq(PackageManager.MATCH_DISABLED_COMPONENTS))).thenReturn(mApplicationInfo);

        final boolean handled =
                mController.onActivityResult(REQUEST_MOCK_LOCATION_APP, Activity.RESULT_OK, intent);

        assertThat(handled).isTrue();
        verify(mAppOpsManager).setMode(anyInt(), anyInt(), eq(newAppName),
                eq(AppOpsManager.MODE_ALLOWED));
        verify(mAppOpsManager).setMode(anyInt(), anyInt(), eq(prevAppName),
                eq(AppOpsManager.MODE_ERRORED));
    }

    @Test
    public void onActivityResult_incorrectCode_shouldReturnFalse() {
        final boolean handled = mController.onActivityResult(30983150 /* request code */,
                Activity.RESULT_OK, null /* intent */);

        assertThat(handled).isFalse();
    }

    @Test
    public void handlePreferenceTreeClick_samePreferenceKey_shouldLaunchActivity() {
        final String preferenceKey = mController.getPreferenceKey();
        when(mPreference.getKey()).thenReturn(preferenceKey);

        final boolean handled = mController.handlePreferenceTreeClick(mPreference);

        assertThat(handled).isTrue();
        verify(mFragment).startActivityForResult(any(), eq(REQUEST_MOCK_LOCATION_APP));
    }

    @Test
    public void handlePreferenceTreeClick_incorrectPreferenceKey_shouldReturnFalse() {
        when(mPreference.getKey()).thenReturn("SomeRandomKey");

        assertThat(mController.handlePreferenceTreeClick(mPreference)).isFalse();
    }

    @Test
    public void onDeveloperOptionsDisabled_currentMockLocationApp_shouldBeNull() {
        mController.onDeveloperOptionsDisabled();

        final String appName = mController.getCurrentMockLocationApp();

        assertThat(appName).isNull();
    }

    private AppOpsManager.OpEntry createOpEntry(int opMode) {
        return new OpEntry(0, opMode, Collections.emptyMap());
    }
}
