import {Component, ViewChild} from '@angular/core';
import {NavigationEnd, Router} from '@angular/router';

import {distinctUntilChanged, filter, map, startWith, tap} from 'rxjs/operators';

import {UntilDestroy, untilDestroyed} from '@ngneat/until-destroy';
import {ITreeOptions, KEYS, TREE_ACTIONS, TreeComponent, TreeModel, TreeNode} from '@circlon/angular-tree-component';

import {CONSTANTS, StringUtils, TreeUtils} from 'm2-core';
import {CdNode} from './CdNode';
import {CD_ROUTING, CD_SECTION_REGEX} from '../../config';
import {formCdNodeId, getContentNodes} from '../../content-config';


@UntilDestroy()
@Component({
    selector: 'cd-docs-tree',
    template: `
        <tree-root [nodes]="rootNodes" [options]="treeOptions" [focused]="true"
                   (initialized)="onTreeInitialized()"
        >
            <ng-template #loadingTemplate i18n="@@loadingMsg">загрузка...</ng-template>

            <!--
                TODO ng16 since rich routerLinkActiveOptions with fragment support have been introduced only in v12,
                we don't use it yet and activate nodes manually here.
                routerLinkActive="tree-node-name-wrapper_active"
            -->
            <ng-template #treeNodeTemplate let-node>
                <a class="tree-node-name-wrapper cd-node"
                   [ngClass]="'cd-node_' + node.data.type"
                   title="{{node.data.displayName}}"
                   routerLink="{{node.data.routerLink}}"
                   [queryParams]="node.data.queryParams"
                   [fragment]="node.data.fragment"
                   [innerHTML]="node.data | highlightedProp:node.data['$$highlighted']"
                >
                </a>
            </ng-template>
        </tree-root>
    `,
    styleUrls: ['./cd-docs-tree.component.scss']
})
export class CdDocsTreeComponent {

    constructor(
        private router: Router
    ) {
    }

    @ViewChild(TreeComponent) treeComponent: TreeComponent;

    rootNodes = getContentNodes();

    /***** Tree configuration *****/
    treeOptions: ITreeOptions = {
        actionMapping: {
            mouse: {
                click: (tree, node, $event) => this.activateNode(tree, node, $event),
                dblClick: TREE_ACTIONS.TOGGLE_EXPANDED
            },
            keys: {
                [KEYS.ENTER]: (tree, node, $event) => this.activateNode(tree, node, $event, true),
                [KEYS.SPACE]: (tree, node, $event) => this.activateNode(tree, node, $event, true)
            }
        }
        // ,idField: CONSTANTS.TREE_NODE_ID_PROPERTY
        ,allowDrop: false
        ,useVirtualScroll: true
        ,nodeHeight: CONSTANTS.TREE_NODE_HEIGHT
        ,dropSlotHeight: 0
    };


    onTreeInitialized(): void {
        this.router.events
            .pipe(
                filter((e): e is NavigationEnd => e instanceof NavigationEnd),
                map(e => e.url),
                startWith(this.router.routerState.snapshot.url),
                distinctUntilChanged(),
                untilDestroyed(this)
            )
            .subscribe(url => {
                const res = CD_SECTION_REGEX.exec(url);
                const treeNodeId = res && formCdNodeId(res[1], res[3]);
                // console.log(`treeNodeId:`, treeNodeId);
                treeNodeId && TreeUtils.activateTreeNode(this.treeComponent, treeNodeId);
            });
    }

    private activateNode(tree, node, $event, navigate = false): void {
        if (!node.isActive) {
            TREE_ACTIONS.TOGGLE_ACTIVE(tree, node, $event);
        }
        if (navigate) {
            this.navigate(node.data);
        }
    }

    private navigate(nodeData: CdNode): void {
        this.router.navigate([`/${CD_ROUTING.BASE}/${nodeData.routerLink}`], { queryParams: nodeData.queryParams });
    }


    /***** Filtering *****/
    applySearchQuery(searchQuery: string): void {
        if (!this.treeComponent) {
            return;
        }
        if (searchQuery) {
            this.filterTree(searchQuery);
        } else {
            this.clearTreeFilter();
        }
    }

    private filterTree(searchQuery: string): void {
        const regex = StringUtils.formSearchRegex(searchQuery);
        const highlightRegex = StringUtils.formHighlightRegex(searchQuery);
        this.treeComponent.treeModel.filterNodes((node: TreeNode) => this.filterNode(node, regex, highlightRegex), true);
    }

    private filterNode(node: TreeNode, regex: RegExp, highlightRegex: RegExp): boolean {
        const cdNode: CdNode = node.data;
        const matches = regex.test([cdNode.displayName, ...cdNode.tags].join(' '));
        cdNode[CONSTANTS.HIGHLIGHTED_PROPERTY_NAME] = matches ?
            StringUtils.highlightByRegex(cdNode.displayName, highlightRegex) :
            undefined;
        return matches/* || node.isActive*/;
    }

    private clearTreeFilter(): void {
        TreeUtils.clearTreeFilter(this.treeComponent);
    }

}
