/*
 * Copyright (C) 2024 The Android Open Source 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.android.launcher3.popup;

import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;

import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.launcher3.AbstractFloatingView.TYPE_SNACKBAR;
import static com.android.launcher3.Flags.FLAG_ENABLE_DISMISS_PREDICTION_UNDO;
import static com.android.launcher3.Flags.FLAG_ENABLE_PRIVATE_SPACE;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_ALL_APPS;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_DISMISS_PREDICTION_UNDO;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_DONT_SUGGEST_APP_TAP;
import static com.android.launcher3.model.data.WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
import android.os.Process;
import android.os.UserHandle;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.view.View;

import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;

import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.R;
import com.android.launcher3.allapps.PrivateProfileManager;
import com.android.launcher3.dagger.LauncherAppComponent;
import com.android.launcher3.dagger.LauncherAppSingleton;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.logging.StatsLogManager.StatsLogger;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.util.AllModulesForTest;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.LauncherModelHelper.SandboxModelContext;
import com.android.launcher3.util.LauncherMultivalentJUnit;
import com.android.launcher3.util.TestSandboxModelContextWrapper;
import com.android.launcher3.util.TestUtil;
import com.android.launcher3.util.UserIconInfo;
import com.android.launcher3.views.Snackbar;
import com.android.launcher3.widget.picker.model.WidgetPickerDataProvider;
import com.android.launcher3.widget.picker.model.data.WidgetPickerData;

import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import dagger.BindsInstance;
import dagger.Component;

@SmallTest
@RunWith(LauncherMultivalentJUnit.class)
public class SystemShortcutTest {
    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
    private static final UserHandle PRIVATE_HANDLE = new UserHandle(11);
    private static final UserHandle MAIN_HANDLE = Process.myUserHandle();
    private View mView;
    private ItemInfo mItemInfo;
    private TestSandboxModelContextWrapper mTestContext;
    private final SandboxModelContext mSandboxContext = new SandboxModelContext();
    private PrivateProfileManager mPrivateProfileManager;
    private WidgetPickerDataProvider mWidgetPickerDataProvider;
    private AppInfo mAppInfo;

    @Mock UserCache mUserCache;
    @Mock UserIconInfo mUserIconInfo;
    @Mock LauncherActivityInfo mLauncherActivityInfo;
    @Mock ApplicationInfo mApplicationInfo;
    @Mock Intent mIntent;
    @Mock StatsLogManager mStatsLogManager;
    @Mock(answer = Answers.RETURNS_SELF) StatsLogger mStatsLogger;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        mSandboxContext.initDaggerComponent(
                DaggerSystemShortcutTest_TestComponent.builder().bindUserCache(mUserCache)
        );
        mTestContext = new TestSandboxModelContextWrapper(mSandboxContext) {
            @Override
            public StatsLogManager getStatsLogManager() {
                return mStatsLogManager;
            }
        };
        spyOn(mSandboxContext);
        doReturn(mStatsLogger).when(mStatsLogManager).logger();

        mView = new View(mTestContext);
        mItemInfo = new ItemInfo();

        LauncherApps mLauncherApps = mSandboxContext.spyService(LauncherApps.class);
        doReturn(mLauncherActivityInfo).when(mLauncherApps).resolveActivity(any(), any());
        when(mLauncherActivityInfo.getApplicationInfo()).thenReturn(mApplicationInfo);

        when(mUserCache.getUserInfo(any())).thenReturn(mUserIconInfo);
        mPrivateProfileManager = mTestContext.getAppsView().getPrivateProfileManager();
        spyOn(mPrivateProfileManager);
        when(mPrivateProfileManager.getProfileUser()).thenReturn(PRIVATE_HANDLE);

        mWidgetPickerDataProvider = mTestContext.getWidgetPickerDataProvider();
        spyOn(mWidgetPickerDataProvider);
    }

    @After
    public void tearDown() {
        mSandboxContext.onDestroy();
    }

    @Test
    public void testWidgetsForNullComponentName() {
        assertNull(mItemInfo.getTargetComponent());
        SystemShortcut systemShortcut = SystemShortcut.WIDGETS
                .getShortcut(mTestContext, mItemInfo, mView);
        assertNull(systemShortcut);
    }

    @Test
    public void testWidgetsForEmptyWidgetList() {
        mAppInfo = new AppInfo();
        mAppInfo.componentName = new ComponentName(mTestContext, getClass());
        assertNotNull(mAppInfo.getTargetComponent());
        doReturn(new WidgetPickerData()).when(mWidgetPickerDataProvider).get();
        spyOn(mAppInfo);
        SystemShortcut systemShortcut = SystemShortcut.WIDGETS
                .getShortcut(mTestContext, mAppInfo, mView);
        verify(mAppInfo, times(2)).getTargetComponent();
        assertNull(systemShortcut);
    }

    @Test
    public void testAppInfoShortcut() {
        mAppInfo = new AppInfo();
        mAppInfo.componentName = new ComponentName(mTestContext, getClass());
        SystemShortcut systemShortcut = SystemShortcut.APP_INFO
                .getShortcut(mTestContext, mAppInfo, mView);
        assertNotNull(systemShortcut);
    }


    @Test
    public void testDontSuggestAppForNonPredictedItem() {
        assertFalse(mItemInfo.isPredictedItem());
        SystemShortcut systemShortcut = SystemShortcut.DONT_SUGGEST_APP
                .getShortcut(mTestContext, mItemInfo, mView);
        assertNull(systemShortcut);
    }

    @Test
    @DisableFlags(FLAG_ENABLE_DISMISS_PREDICTION_UNDO)
    public void testDontSuggestAppForPredictedItem() {
        mAppInfo = new AppInfo();
        mAppInfo.componentName = new ComponentName(mTestContext, getClass());
        mAppInfo.container = CONTAINER_HOTSEAT_PREDICTION;
        assertTrue(mAppInfo.isPredictedItem());
        SystemShortcut systemShortcut = SystemShortcut.DONT_SUGGEST_APP
                .getShortcut(mTestContext, mAppInfo, mView);
        assertNotNull(systemShortcut);

        TestUtil.runOnExecutorSync(MAIN_EXECUTOR, () -> systemShortcut.onClick(mView));
        InstrumentationRegistry.getInstrumentation().waitForIdleSync();

        verify(mStatsLogger).log(eq(LAUNCHER_SYSTEM_SHORTCUT_DONT_SUGGEST_APP_TAP));
        assertFalse(AbstractFloatingView.hasOpenView(mTestContext, TYPE_SNACKBAR));
    }

    @Test
    @EnableFlags(FLAG_ENABLE_DISMISS_PREDICTION_UNDO)
    public void testDontSuggestAppForPredictedItemWithUndo() {
        mAppInfo = new AppInfo();
        mAppInfo.componentName = new ComponentName(mTestContext, getClass());
        mAppInfo.container = CONTAINER_HOTSEAT_PREDICTION;
        assertTrue(mAppInfo.isPredictedItem());
        SystemShortcut systemShortcut = SystemShortcut.DONT_SUGGEST_APP
                .getShortcut(mTestContext, mAppInfo, mView);
        assertNotNull(systemShortcut);

        TestUtil.runOnExecutorSync(MAIN_EXECUTOR, () -> systemShortcut.onClick(mView));
        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
        verify(mStatsLogger).log(eq(LAUNCHER_SYSTEM_SHORTCUT_DONT_SUGGEST_APP_TAP));

        // Undo bar shown
        Snackbar snackbar = AbstractFloatingView.getOpenView(mTestContext, TYPE_SNACKBAR);
        assertNotNull(snackbar);
        reset(mStatsLogger);
        TestUtil.runOnExecutorSync(MAIN_EXECUTOR, snackbar.findViewById(
                R.id.action)::performClick);
        verify(mStatsLogger).log(eq(LAUNCHER_DISMISS_PREDICTION_UNDO));
    }

    @Test
    public void testPrivateProfileInstallwithTargetComponentNull() {
        assertNull(mItemInfo.getTargetComponent());
        SystemShortcut systemShortcut = SystemShortcut.PRIVATE_PROFILE_INSTALL
                .getShortcut(mTestContext, mItemInfo, mView);
        assertNull(systemShortcut);
    }

    @Test
    public void testPrivateProfileInstallNotAllAppsContainer() {
        mAppInfo = new AppInfo();
        mAppInfo.componentName = new ComponentName(mTestContext, getClass());
        mAppInfo.container = CONTAINER_HOTSEAT_PREDICTION;

        assertNotNull(mAppInfo.getTargetComponent());
        assertFalse(mAppInfo.getContainerInfo().hasAllAppsContainer());

        SystemShortcut systemShortcut = SystemShortcut.PRIVATE_PROFILE_INSTALL
                .getShortcut(mTestContext, mAppInfo, mView);
        assertNull(systemShortcut);
    }

    @Test
    public void testPrivateProfileInstallNullPrivateProfileManager() {
        mAppInfo = new AppInfo();
        mAppInfo.componentName = new ComponentName(mTestContext, getClass());
        mAppInfo.container = CONTAINER_ALL_APPS;
        mPrivateProfileManager = null;

        assertNotNull(mAppInfo.getTargetComponent());
        assertTrue(mAppInfo.getContainerInfo().hasAllAppsContainer());
        assertNull(mPrivateProfileManager);

        SystemShortcut systemShortcut = SystemShortcut.PRIVATE_PROFILE_INSTALL
                .getShortcut(mTestContext, mAppInfo, mView);
        assertNull(systemShortcut);
    }

    @Test
    public void testPrivateProfileInstallPrivateProfileManagerDisabled() {
        mAppInfo = new AppInfo();
        mAppInfo.componentName = new ComponentName(mTestContext, getClass());
        mAppInfo.container = CONTAINER_ALL_APPS;

        assertNotNull(mPrivateProfileManager);
        assertNotNull(mAppInfo.getTargetComponent());
        assertTrue(mAppInfo.getContainerInfo().hasAllAppsContainer());

        when(mPrivateProfileManager.isEnabled()).thenReturn(false);
        SystemShortcut systemShortcut = SystemShortcut.PRIVATE_PROFILE_INSTALL
                .getShortcut(mTestContext, mAppInfo, mView);
        assertNull(systemShortcut);
    }

    @Test
    public void testPrivateProfileInstallNullPrivateProfileUser() {
        mAppInfo = new AppInfo();
        mAppInfo.componentName = new ComponentName(mTestContext, getClass());
        mAppInfo.container = CONTAINER_ALL_APPS;
        when(mPrivateProfileManager.getProfileUser()).thenReturn(null);

        assertNotNull(mPrivateProfileManager);
        assertNotNull(mAppInfo.getTargetComponent());
        assertTrue(mAppInfo.getContainerInfo().hasAllAppsContainer());
        assertNull(mPrivateProfileManager.getProfileUser());

        SystemShortcut systemShortcut = SystemShortcut.PRIVATE_PROFILE_INSTALL
                .getShortcut(mTestContext, mAppInfo, mView);

        assertNull(systemShortcut);
    }

    @Test
    public void testPrivateProfileInstallNonNullPrivateProfileUser() {
        mAppInfo = new AppInfo();
        mAppInfo.componentName = new ComponentName(mTestContext, getClass());
        mAppInfo.container = CONTAINER_ALL_APPS;
        when(mPrivateProfileManager.isEnabled()).thenReturn(true);
        when(mPrivateProfileManager.getProfileUser()).thenReturn(PRIVATE_HANDLE);

        assertNotNull(mAppInfo.getTargetComponent());
        assertTrue(mAppInfo.getContainerInfo().hasAllAppsContainer());
        assertNotNull(mPrivateProfileManager);
        assertNotNull(mPrivateProfileManager.getProfileUser());
        assertNull(mTestContext.getAppsView().getAppsStore().getApp(
                new ComponentKey(mAppInfo.getTargetComponent(), PRIVATE_HANDLE)));

        SystemShortcut systemShortcut = SystemShortcut.PRIVATE_PROFILE_INSTALL
                .getShortcut(mTestContext, mAppInfo, mView);

        verify(mPrivateProfileManager, atLeast(1)).isEnabled();
        assertNotNull(systemShortcut);
    }

    @Test
    public void testInstallGetShortcutWithNonWorkSpaceItemInfo() {
        SystemShortcut systemShortcut = SystemShortcut.INSTALL.getShortcut(
                mTestContext, mItemInfo, mView);
        Assert.assertNull(systemShortcut);
    }

    @Test
    @UiThreadTest
    public void testInstallGetShortcutWithWorkSpaceItemInfo() {
        mAppInfo = new AppInfo();
        mAppInfo.componentName = new ComponentName(mTestContext, getClass());
        mAppInfo.intent = mIntent;
        WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo(mAppInfo);
        workspaceItemInfo.status = FLAG_SUPPORTS_WEB_UI;
        SystemShortcut systemShortcut = SystemShortcut.INSTALL.getShortcut(
                mTestContext, workspaceItemInfo, mView);
        Assert.assertNotNull(systemShortcut);
    }


    @Test
    @DisableFlags(FLAG_ENABLE_PRIVATE_SPACE)
    public void testUninstallGetShortcutWithPrivateSpaceOff() {
        SystemShortcut systemShortcut = SystemShortcut.UNINSTALL_APP.getShortcut(
                mTestContext, null, mView);
        Assert.assertNull(systemShortcut);
    }

    @Test
    @EnableFlags(FLAG_ENABLE_PRIVATE_SPACE)
    public void testUninstallGetShortcutWithNonPrivateItemInfo() {
        mAppInfo = new AppInfo();
        mAppInfo.user = MAIN_HANDLE;
        when(mUserIconInfo.isPrivate()).thenReturn(false);

        SystemShortcut systemShortcut = SystemShortcut.UNINSTALL_APP.getShortcut(
                mTestContext, mAppInfo, mView);
        verify(mUserIconInfo).isPrivate();
        Assert.assertNull(systemShortcut);
    }

    @Test
    @EnableFlags(FLAG_ENABLE_PRIVATE_SPACE)
    public void testUninstallGetShortcutWithSystemItemInfo() {
        mAppInfo = new AppInfo();
        mAppInfo.user = PRIVATE_HANDLE;
        mAppInfo.itemType = ITEM_TYPE_APPLICATION;
        mAppInfo.intent = mIntent;
        mAppInfo.componentName = new ComponentName(mTestContext, getClass());
        when(mLauncherActivityInfo.getComponentName()).thenReturn(mAppInfo.componentName);
        when(mUserIconInfo.isPrivate()).thenReturn(true);
        // System App
        mApplicationInfo.flags = 1;

        SystemShortcut systemShortcut = SystemShortcut.UNINSTALL_APP.getShortcut(
                mTestContext, mAppInfo, mView);
        verify(mLauncherActivityInfo, times(0)).getComponentName();
        Assert.assertNull(systemShortcut);
    }

    @Test
    @EnableFlags(FLAG_ENABLE_PRIVATE_SPACE)
    public void testUninstallGetShortcutWithPrivateItemInfo() {
        mAppInfo = new AppInfo();
        mAppInfo.user = PRIVATE_HANDLE;
        mAppInfo.itemType = ITEM_TYPE_APPLICATION;
        mAppInfo.intent = mIntent;
        mAppInfo.componentName = new ComponentName(mTestContext, getClass());
        when(mUserIconInfo.isPrivate()).thenReturn(true);
        when(mLauncherActivityInfo.getComponentName()).thenReturn(mAppInfo.componentName);
        // 3rd party app, not system app.
        mApplicationInfo.flags = 0;

        SystemShortcut systemShortcut = SystemShortcut.UNINSTALL_APP.getShortcut(
                mTestContext, mAppInfo, mView);

        verify(mLauncherActivityInfo).getComponentName();
        Assert.assertNotNull(systemShortcut);

        systemShortcut.onClick(mView);
        verify(mSandboxContext).startActivity(any());
    }

    @LauncherAppSingleton
    @Component(modules = { AllModulesForTest.class })
    interface TestComponent extends LauncherAppComponent {
        @Component.Builder
        interface Builder extends LauncherAppComponent.Builder {
            @BindsInstance
            SystemShortcutTest.TestComponent.Builder bindUserCache(UserCache userCache);
            @Override LauncherAppComponent build();
        }
    }
}
