// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chrome.browser.bookmarks;

import android.app.Activity;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import org.chromium.base.supplier.ObservableSupplier;
import org.chromium.base.supplier.Supplier;
import org.chromium.chrome.browser.offlinepages.OfflinePageUtils;
import org.chromium.chrome.browser.read_later.ReadingListUtils;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
import org.chromium.components.bookmarks.BookmarkId;
import org.chromium.components.bookmarks.BookmarkItem;
import org.chromium.components.bookmarks.BookmarkType;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;

/**
 * Helper class for managing the UI flow for bookmarking the active tab and kicking off the backend.
 * Shows a snackbar if a new bookmark was added. If the bookmark already exists, kicks off edit
 * bookmark UI. Includes price tracking specific UI if the page is relevant for price tracking.
 */
public class TabBookmarker {
    private final Activity mActivity;
    private final Supplier<BookmarkModel> mBookmarkModelSupplier;
    private final Supplier<BottomSheetController> mBottomSheetControllerSupplier;
    private final Supplier<SnackbarManager> mSnackbarManagerSupplier;
    private final boolean mIsCustomTab;

    /**
     * Constructor.
     * @param activity The current activity.
     * @param bookmarkModelSupplier Supplier of the bookmark bridge for the current profile.
     * @param bottomSheetControllerSupplier Supplier of the {@link BottomSheetController} for this
     *         activity.
     * @param snackbarManagerSupplier Supplier of the {@link SnackbarManager}.
     * @param isCustomTab Whether this is a custom tab activity.
     */
    public TabBookmarker(@NonNull Activity activity,
            @NonNull ObservableSupplier<BookmarkModel> bookmarkModelSupplier,
            @NonNull Supplier<BottomSheetController> bottomSheetControllerSupplier,
            @NonNull Supplier<SnackbarManager> snackbarManagerSupplier, boolean isCustomTab) {
        mActivity = activity;
        mBookmarkModelSupplier = bookmarkModelSupplier;
        mBottomSheetControllerSupplier = bottomSheetControllerSupplier;
        mSnackbarManagerSupplier = snackbarManagerSupplier;
        mIsCustomTab = isCustomTab;
    }

    /**
     * Add the specified tab to bookmarks or allows to edit the bookmark if the specified tab is
     * already bookmarked. If a new bookmark is added, a snackbar will be shown.
     *
     * @param tabToBookmark The tab that needs to be bookmarked.
     */
    public void addOrEditBookmark(final Tab tabToBookmark) {
        addOrEditBookmark(tabToBookmark, BookmarkType.NORMAL, /*fromExplicitTrackUi=*/false);
    }

    /**
     * Adds the specified tab to the Reading List. Opens a new item if an item was added. Opens UI
     * for editing the Reading List item if it was already present on the list.
     *
     * @param tabToAdd The tab that to add to the Reading List.
     */
    public void addToReadingList(final Tab tabToAdd) {
        addOrEditBookmark(tabToAdd, BookmarkType.READING_LIST, /*fromExplicitTrackUi=*/false);
    }

    /**
     * Starts price tracking for the current tab. If the page is already being price tracked, the
     * edit price tracking flow will start.
     * @param currentTab The tab being currently shown.
     */
    public void startOrModifyPriceTracking(Tab currentTab) {
        BookmarkId bookmarkId = mBookmarkModelSupplier.get().getUserBookmarkIdForTab(currentTab);
        if (bookmarkId == null) {
            addOrEditBookmark(currentTab, BookmarkType.NORMAL, /* fromExplicitTrackUi=*/true);
        } else {
            // In the case where the bookmark exists, re-show the save flow with price-tracking
            // enabled.
            BookmarkUtils.showSaveFlow(mActivity, mBottomSheetControllerSupplier.get(),
                    /*fromExplicitTrackUi=*/true, bookmarkId, /*wasBookmarkMoved=*/false,
                    /*isNewBookmark=*/false);
        }
    }

    private void addOrEditBookmark(
            final Tab tabToBookmark, @BookmarkType int bookmarkType, boolean fromExplicitTrackUi) {
        if (tabToBookmark == null || tabToBookmark.isFrozen()) {
            return;
        }

        // Defense in depth against the UI being erroneously enabled.
        final BookmarkModel bookmarkModel = mBookmarkModelSupplier.get();
        if (bookmarkModel == null || !bookmarkModel.isEditBookmarksEnabled()) {
            assert false;
            return;
        }

        bookmarkModel.finishLoadingBookmarkModel(() -> {
            // Gives up the bookmarking if the tab is being destroyed.
            if (tabToBookmark.isClosing() || !tabToBookmark.isInitialized()
                    || mBottomSheetControllerSupplier.get() == null
                    || mSnackbarManagerSupplier.get() == null) {
                return;
            }

            BookmarkId bookmarkId = bookmarkModel.getUserBookmarkIdForTab(tabToBookmark);
            boolean isNewBookmark = bookmarkId == null;
            if (ReadingListUtils.maybeTypeSwapAndShowSaveFlow(mActivity,
                        mBottomSheetControllerSupplier.get(), bookmarkModel, bookmarkId,
                        bookmarkType)) {
                return;
            }

            BookmarkItem currentBookmarkItem =
                    bookmarkId == null ? null : bookmarkModel.getBookmarkById(bookmarkId);
            onBookmarkModelLoaded(tabToBookmark, currentBookmarkItem, bookmarkModel, bookmarkType,
                    fromExplicitTrackUi, isNewBookmark);
        });
    }

    private void onBookmarkModelLoaded(final Tab tabToBookmark,
            @Nullable final BookmarkItem currentBookmarkItem, final BookmarkModel bookmarkModel,
            @BookmarkType int bookmarkType, boolean fromExplicitTrackUi, boolean isNewBookmark) {
        BookmarkUtils.addOrEditBookmark(currentBookmarkItem, bookmarkModel, tabToBookmark,
                mSnackbarManagerSupplier.get(), mBottomSheetControllerSupplier.get(), mActivity,
                mIsCustomTab, bookmarkType, (newBookmarkId) -> {
                    BookmarkId currentBookmarkId =
                            (currentBookmarkItem == null) ? null : currentBookmarkItem.getId();
                    // Add offline page for a new bookmark.
                    if (newBookmarkId != null && !newBookmarkId.equals(currentBookmarkId)) {
                        OfflinePageUtils.saveBookmarkOffline(newBookmarkId, tabToBookmark);
                    }
                }, fromExplicitTrackUi);
    }
}
