// (C) Copyright 2015 Moodle Pty Ltd.
//
// 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.

import {
    Component,
    Input,
    OnInit,
    OnChanges,
    OnDestroy,
    AfterViewInit,
    ViewChild,
    ElementRef,
    SimpleChange,
} from '@angular/core';
import { IonTabs } from '@ionic/angular';
import { Subscription } from 'rxjs';

import { CoreUtils } from '@services/utils/utils';
import { Params } from '@angular/router';
import { CoreNavBarButtonsComponent } from '../navbar-buttons/navbar-buttons';
import { CoreDomUtils } from '@services/utils/dom';
import { StackEvent } from '@ionic/angular/directives/navigation/stack-utils';
import { CoreNavigator } from '@services/navigator';
import { CoreTabBase, CoreTabsBaseComponent } from '@classes/tabs';

/**
 * This component displays some top scrollable tabs that will autohide on vertical scroll.
 * Each tab will load a page using Angular router.
 *
 * Example usage:
 *
 * <core-tabs-outlet selectedIndex="1" [tabs]="tabs"></core-tabs-outlet>
 *
 * Tab contents will only be shown if that tab is selected.
 *
 * @todo: Test RTL and tab history.
 * @todo: This should behave like the split-view in relation to routing (maybe we could reuse some code from CoreItemsListManager).
 */
@Component({
    selector: 'core-tabs-outlet',
    templateUrl: 'core-tabs-outlet.html',
    styleUrls: ['../tabs/tabs.scss'],
})
export class CoreTabsOutletComponent extends CoreTabsBaseComponent<CoreTabsOutletTab>
    implements OnInit, AfterViewInit, OnChanges, OnDestroy {

    /**
     * Determine tabs layout.
     */
    @Input() layout: 'icon-top' | 'icon-start' | 'icon-end' | 'icon-bottom' | 'icon-hide' | 'label-hide' = 'icon-hide';
    @Input() tabs: CoreTabsOutletTab[] = [];

    @ViewChild(IonTabs) protected ionTabs?: IonTabs;

    protected stackEventsSubscription?: Subscription;

    constructor(
        element: ElementRef,
    ) {
        super(element);
    }

    /**
     * Init tab info.
     *
     * @param tab Tab.
     */
    protected initTab(tab: CoreTabsOutletTab): void {
        tab.id = tab.id || 'core-tab-outlet-' + CoreUtils.getUniqueId('CoreTabsOutletComponent');
        if (typeof tab.enabled == 'undefined') {
            tab.enabled = true;
        }
    }

    /**
     * View has been initialized.
     */
    async ngAfterViewInit(): Promise<void> {
        super.ngAfterViewInit();

        if (this.isDestroyed) {
            return;
        }

        this.tabsElement = this.element.nativeElement.querySelector('ion-tabs');
        this.stackEventsSubscription = this.ionTabs?.outlet.stackEvents.subscribe(async (stackEvent: StackEvent) => {
            if (!this.isCurrentView) {
                return;
            }

            this.showHideNavBarButtons(stackEvent.enteringView.element.tagName);

            await this.listenContentScroll(stackEvent.enteringView.element, stackEvent.enteringView.id);

            const scrollElement = this.scrollElements[stackEvent.enteringView.id];
            if (scrollElement) {
                // Show or hide tabs based on the new page scroll.
                this.showHideTabs(scrollElement.scrollTop, scrollElement);
            }
        });
    }

    /**
     * Detect changes on input properties.
     */
    ngOnChanges(changes: Record<string, SimpleChange>): void {
        if (changes.tabs) {
            this.tabs.forEach((tab) => {
                this.initTab(tab);
            });

            this.calculateSlides();
        }

        super.ngOnChanges(changes);
    }

    /**
     * Load the tab.
     *
     * @param tabToSelect Tab to load.
     * @return Promise resolved with true if tab is successfully loaded.
     */
    protected async loadTab(tabToSelect: CoreTabsOutletTab): Promise<boolean> {
        return CoreNavigator.navigate(tabToSelect.page, {
            params: tabToSelect.pageParams,
        });
    }

    /**
     * Get all child core-navbar-buttons and show or hide depending on the page state.
     * We need to use querySelectorAll because ContentChildren doesn't work with ng-template.
     * https://github.com/angular/angular/issues/14842
     *
     * @param activatedPageName Activated page name.
     */
    protected showHideNavBarButtons(activatedPageName: string): void {
        const elements = this.ionTabs!.outlet.nativeEl.querySelectorAll('core-navbar-buttons');
        const domUtils = CoreDomUtils.instance;
        elements.forEach((element) => {
            const instance: CoreNavBarButtonsComponent = domUtils.getInstanceByElement(element);

            if (instance) {
                const pagetagName = element.closest('.ion-page')?.tagName;
                instance.forceHide(activatedPageName != pagetagName);
            }
        });
    }

    /**
     * Component destroyed.
     */
    ngOnDestroy(): void {
        super.ngOnDestroy();
        this.stackEventsSubscription?.unsubscribe();
    }

}

/**
 * Tab to be displayed in CoreTabsOutlet.
 */
export type CoreTabsOutletTab = CoreTabBase & {
    page: string; // Page to navigate to.
    pageParams?: Params; // Page params.
};