// Copyright 2020 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.page_info;

import android.content.res.Resources;
import android.text.format.DateUtils;
import android.view.View;
import android.view.ViewGroup;

import org.chromium.base.ResettersForTesting;
import org.chromium.base.supplier.ObservableSupplierImpl;
import org.chromium.base.supplier.Supplier;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.history.BrowsingHistoryBridge;
import org.chromium.chrome.browser.history.HistoryContentManager;
import org.chromium.chrome.browser.history.HistoryItem;
import org.chromium.chrome.browser.history.HistoryProvider;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.components.browser_ui.util.date.CalendarUtils;
import org.chromium.components.browser_ui.util.date.StringUtils;
import org.chromium.components.page_info.PageInfoAction;
import org.chromium.components.page_info.PageInfoControllerDelegate;
import org.chromium.components.page_info.PageInfoMainController;
import org.chromium.components.page_info.PageInfoRowView;
import org.chromium.components.page_info.PageInfoSubpageController;

import java.util.Date;

/**
 * Class for controlling the page info history section.
 */
public class PageInfoHistoryController
        implements PageInfoSubpageController, HistoryContentManager.Observer {
    public static final int HISTORY_ROW_ID = View.generateViewId();

    private static HistoryProvider sProviderForTests;
    /** Clock to use so we can mock time in tests. */
    public interface Clock {
        long currentTimeMillis();
    }
    private static Clock sClock = System::currentTimeMillis;

    private final PageInfoMainController mMainController;
    private final PageInfoRowView mRowView;
    private final PageInfoControllerDelegate mDelegate;
    private final Supplier<Tab> mTabSupplier;
    private final String mTitle;
    private final String mHost;
    private boolean mDataIsStale;
    private HistoryProvider mHistoryProvider;
    private HistoryContentManager mContentManager;
    private long mLastVisitedTimestamp;

    public PageInfoHistoryController(PageInfoMainController mainController, PageInfoRowView rowView,
            PageInfoControllerDelegate delegate, Supplier<Tab> tabSupplier) {
        mMainController = mainController;
        mRowView = rowView;
        mDelegate = delegate;
        mTitle = mRowView.getContext().getResources().getString(R.string.page_info_history_title);
        mHost = mainController.getURL().getHost();
        mTabSupplier = tabSupplier;

        updateLastVisit();
    }

    private void launchSubpage() {
        mMainController.recordAction(PageInfoAction.PAGE_INFO_HISTORY_OPENED);
        mMainController.launchSubpage(this);
    }

    @Override
    public String getSubpageTitle() {
        return mTitle;
    }

    @Override
    public View createViewForSubpage(ViewGroup parent) {
        assert !mDelegate.isIncognito();
        mContentManager = new HistoryContentManager(mMainController.getActivity(), this,
                /* isSeparateActivity */ false,
                /* isIncognito */ false, /* shouldShowPrivacyDisclaimers */ true,
                /* shouldShowClearData */ false, mHost,
                /* selectionDelegate */ null, mTabSupplier, new ObservableSupplierImpl<>(),
                vg -> null, new BrowsingHistoryBridge(Profile.getLastUsedRegularProfile()));
        mContentManager.startLoadingItems();
        return mContentManager.getRecyclerView();
    }

    @Override
    public void onSubpageRemoved() {
        if (mContentManager != null) {
            mContentManager.onDestroyed();
            mContentManager = null;
        }
    }

    private void updateLastVisit() {
        mHistoryProvider = sProviderForTests != null
                ? sProviderForTests
                : new BrowsingHistoryBridge(Profile.getLastUsedRegularProfile());
        mHistoryProvider.getLastVisitToHostBeforeRecentNavigations(mHost, (timestamp) -> {
            mLastVisitedTimestamp = timestamp;
            if (mHistoryProvider != null) {
                mHistoryProvider.destroy();
                mHistoryProvider = null;
            }
            setupHistoryRow();
        });
    }

    private void setupHistoryRow() {
        PageInfoRowView.ViewParams rowParams = new PageInfoRowView.ViewParams();
        rowParams.title = getRowTitle();
        rowParams.visible = rowParams.title != null && mDelegate.isSiteSettingsAvailable()
                && !mDelegate.isIncognito();
        rowParams.iconResId = R.drawable.ic_history_googblue_24dp;
        rowParams.decreaseIconSize = true;
        rowParams.clickCallback = this::launchSubpage;

        mRowView.setParams(rowParams);
    }

    private String getRowTitle() {
        if (mLastVisitedTimestamp == 0) {
            return null;
        }
        long today = CalendarUtils.getStartOfDay(sClock.currentTimeMillis()).getTime().getTime();
        long lastVisitedDay =
                CalendarUtils.getStartOfDay(mLastVisitedTimestamp).getTime().getTime();
        long difference = today - lastVisitedDay;
        Resources resources = mRowView.getContext().getResources();
        if (difference < 0) {
            return null;
        } else if (difference == 0) {
            return resources.getString(R.string.page_info_history_last_visit_today);
        } else if (difference == DateUtils.DAY_IN_MILLIS) {
            return resources.getString(R.string.page_info_history_last_visit_yesterday);
        } else if (difference > DateUtils.DAY_IN_MILLIS
                && difference <= DateUtils.DAY_IN_MILLIS * 7) {
            return resources.getString(R.string.page_info_history_last_visit_days,
                    (int) (difference / DateUtils.DAY_IN_MILLIS));
        } else {
            return resources.getString(R.string.page_info_history_last_visit_date,
                    StringUtils.dateToHeaderString(new Date(mLastVisitedTimestamp)));
        }
    }

    @Override
    public void clearData() {
        // TODO(crbug.com/1173154): Add functionality for clear history for this site.
        return;
    }

    @Override
    public void updateRowIfNeeded() {
        if (mDataIsStale) {
            updateLastVisit();
        }
        mDataIsStale = false;
    }

    // HistoryContentManager.Observer
    @Override
    public void onScrolledCallback(boolean loadedMore) {}

    // HistoryContentManager.Observer
    @Override
    public void onItemClicked(HistoryItem item) {
        mMainController.recordAction(PageInfoAction.PAGE_INFO_HISTORY_ENTRY_CLICKED);
        return;
    }

    // HistoryContentManager.Observer
    @Override
    public void onItemRemoved(HistoryItem item) {
        mMainController.recordAction(PageInfoAction.PAGE_INFO_HISTORY_ENTRY_REMOVED);
        mDataIsStale = true;
        if (mContentManager.getItemCount() == 0) {
            // Do the update right away if there are no entries left.
            mLastVisitedTimestamp = 0;
            setupHistoryRow();
            mMainController.exitSubpage();
        }
    }

    // HistoryContentManager.Observer
    @Override
    public void onClearBrowsingDataClicked() {
        // TODO(crbug.com/1173154): Add functionality for "clear history" button click and
        // change the name of the current clear browsing data button.
        return;
    }

    // HistoryContentManager.Observer
    @Override
    public void onPrivacyDisclaimerHasChanged() {}

    // HistoryContentManager.Observer
    @Override
    public void onUserAccountStateChanged() {}

    // HistoryContentManager.Observer
    @Override
    public void onHistoryDeletedExternally() {}

    /** @param provider The {@link HistoryProvider} that is used in place of a real one. */
    public static void setProviderForTests(HistoryProvider provider) {
        sProviderForTests = provider;
        ResettersForTesting.register(() -> sProviderForTests = null);
    }

    static void setClockForTesting(Clock clock) {
        var oldValue = sClock;
        sClock = clock;
        ResettersForTesting.register(() -> sClock = oldValue);
    }
}
