// 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.
import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
import 'chrome://resources/cr_elements/cr_icons.css.js';
import 'chrome://resources/cr_elements/cr_shared_style.css.js';
import './shared_style.css.js';
import './site_favicon.js';
import './credential_details/password_details_card.js';
import './credential_details/passkey_details_card.js';
import './user_utils_mixin.js';
import { PrefsMixin } from '/shared/settings/prefs/prefs_mixin.js';
import { assert } from 'chrome://resources/js/assert.js';
import { PolymerElement } from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import { getTemplate } from './password_details_section.html.js';
import { PasswordManagerImpl, PasswordViewPageInteractions } from './password_manager_proxy.js';
import { Page, RouteObserverMixin, Router } from './router.js';
import { UserUtilMixin } from './user_utils_mixin.js';
const PasswordDetailsSectionElementBase = PrefsMixin(UserUtilMixin(RouteObserverMixin(PolymerElement)));
export class PasswordDetailsSectionElement extends PasswordDetailsSectionElementBase {
    static get is() {
        return 'password-details-section';
    }
    static get template() {
        return getTemplate();
    }
    static get properties() {
        return {
            selectedGroup_: {
                type: Object,
            },
        };
    }
    savedPasswordsListener_ = null;
    passwordManagerAuthTimeoutListener_;
    visibilityChangedListener_;
    connectedCallback() {
        super.connectedCallback();
        this.passwordManagerAuthTimeoutListener_ = () => {
            if (Router.getInstance().currentRoute.page !== Page.PASSWORD_DETAILS) {
                return;
            }
            this.dispatchEvent(new CustomEvent('auth-timed-out', {
                bubbles: true,
                composed: true,
            }));
            this.navigateBack_();
            PasswordManagerImpl.getInstance().recordPasswordViewInteraction(PasswordViewPageInteractions.TIMED_OUT_IN_VIEW_PAGE);
        };
        PasswordManagerImpl.getInstance().addPasswordManagerAuthTimeoutListener(this.passwordManagerAuthTimeoutListener_);
    }
    disconnectedCallback() {
        super.disconnectedCallback();
        if (this.savedPasswordsListener_) {
            PasswordManagerImpl.getInstance().removeSavedPasswordListChangedListener(this.savedPasswordsListener_);
            this.savedPasswordsListener_ = null;
        }
        PasswordManagerImpl.getInstance().removePasswordManagerAuthTimeoutListener(this.passwordManagerAuthTimeoutListener_);
    }
    currentRouteChanged(route, _) {
        if (route.page !== Page.PASSWORD_DETAILS) {
            this.selectedGroup_ = undefined;
            return;
        }
        const group = route.details;
        if (group && group.name) {
            this.selectedGroup_ = group;
            this.startListeningForUpdates_();
            setTimeout(() => {
                this.$.backButton.focus();
            });
        }
        else {
            // Navigation happened directly. Find group with matching name.
            PasswordManagerImpl.getInstance().recordPasswordViewInteraction(PasswordViewPageInteractions.CREDENTIAL_REQUESTED_BY_URL);
            this.assignMatchingGroup(route.details);
        }
    }
    navigateBack_() {
        // Keep search query when navigating back.
        Router.getInstance().navigateTo(Page.PASSWORDS, null, Router.getInstance().currentRoute.queryParameters);
    }
    async assignMatchingGroup(groupName) {
        const groups = await PasswordManagerImpl.getInstance().getCredentialGroups();
        let selectedGroup = groups.find(group => group.name === groupName);
        if (!selectedGroup) {
            // Check if any password in a group has matching domain.
            selectedGroup = groups.find(group => group.entries.some(entry => entry.affiliatedDomains?.some(domain => domain.name === groupName)));
        }
        if (!selectedGroup) {
            this.navigateBack_();
            PasswordManagerImpl.getInstance().recordPasswordViewInteraction(PasswordViewPageInteractions.CREDENTIAL_NOT_FOUND);
            return;
        }
        assert(selectedGroup);
        this.updateShownCredentials(selectedGroup)
            .then(this.startListeningForUpdates_.bind(this))
            .catch(this.navigateBack_);
        PasswordManagerImpl.getInstance().recordPasswordViewInteraction(PasswordViewPageInteractions.CREDENTIAL_FOUND);
    }
    startListeningForUpdates_() {
        if (this.savedPasswordsListener_) {
            return;
        }
        this.savedPasswordsListener_ = _passwordList => {
            PasswordManagerImpl.getInstance().getCredentialGroups().then(this.refreshGroupInfo_.bind(this));
        };
        PasswordManagerImpl.getInstance().addSavedPasswordListChangedListener(this.savedPasswordsListener_);
    }
    /*
     * Requests passwords and notes for all credentials from a group. If page
     * isn't visible the request will be postponed until tab becomes focused
     * again. This is done to prevent unnecessary authentication prompts.
     */
    updateShownCredentials(group) {
        if (document.visibilityState === 'visible') {
            return this.requestShownCredentials_(group);
        }
        return new Promise((resolve, reject) => {
            this.visibilityChangedListener_ = () => {
                if (document.visibilityState === 'visible') {
                    document.removeEventListener('visibilitychange', this.visibilityChangedListener_);
                    this.requestShownCredentials_(group).then(resolve).catch(reject);
                }
            };
            document.addEventListener('visibilitychange', this.visibilityChangedListener_);
        });
    }
    requestShownCredentials_(group) {
        const ids = group.entries.map(entry => entry.id);
        return PasswordManagerImpl.getInstance()
            .requestCredentialsDetails(ids)
            .then(entries => {
            group.entries = entries;
            this.selectedGroup_ = group;
        });
    }
    /*
     * Credentials have changed, check if shown credentials still exist:
     * if yes, navigates to its group page
     * if no, navigate back to Passwords page.
     */
    refreshGroupInfo_(groups) {
        if (!this.selectedGroup_) {
            // It's possible refresh was triggered during page opening or closure when
            // `selectedGroup_` is not set. In this case do nothing.
            return;
        }
        const currentIds = this.selectedGroup_.entries.map(entry => entry.id);
        let matchingGroup = groups.filter(group => group.entries.some(entry => currentIds.includes(entry.id)))[0];
        // If there is no group with matching id for PasswordUIEntry it means that
        // either PasswordUIEntry is deleted or updated. During update, site value
        // can't change so there should be a group with the same name.
        if (!matchingGroup) {
            matchingGroup =
                groups.filter(group => group.name === this.selectedGroup_.name)[0];
            // If no group with matching name can be found it means that
            // PasswordUIEntry was deleted and group no longer exists.
            if (!matchingGroup) {
                this.navigateBack_();
                return;
            }
        }
        assert(matchingGroup);
        const newIds = matchingGroup.entries.map(entry => entry.id);
        const currentStores = this.selectedGroup_.entries.map(entry => entry.storedIn);
        const newStores = matchingGroup.entries.map(entry => entry.storedIn);
        const currentBackups = this.selectedGroup_.entries.map(entry => entry.backupPassword);
        const newBackups = matchingGroup.entries.map(entry => entry.backupPassword);
        // If ids match and stores used for entries haven't changed, don't do
        // anything.
        if (currentIds.sort().toString() === newIds.sort().toString() &&
            currentStores.sort().toString() === newStores.sort().toString() &&
            currentBackups.sort().toString() === newBackups.sort().toString()) {
            return;
        }
        this.updateShownCredentials(matchingGroup)
            .then(() => {
            // Use navigation to update page title if needed.
            Router.getInstance().navigateTo(Page.PASSWORD_DETAILS, this.selectedGroup_, Router.getInstance().currentRoute.queryParameters);
        })
            .catch(this.navigateBack_);
    }
    /*
     * Only register the first card to show the Password Sharing Help Bubble.
     */
    shouldRegisterSharingPromo_(index) {
        return index === 0;
    }
}
customElements.define(PasswordDetailsSectionElement.is, PasswordDetailsSectionElement);
