import {Component, Input, OnInit, ViewChild} from '@angular/core';
import {IonInput, ModalController} from '@ionic/angular';
import {ApiService} from '../../service/api.service';
import {BehaviorSubject} from 'rxjs';
import {Router} from '@angular/router';
import {NodeTypeIcons} from '../../structures/node-types';
import {debounce, debug} from '../../utility';
import {OpenerService} from '../../service/opener.service';

export interface SearchResult {
    title: string;
    short_title: string;
    type: string;
    id: string;
    boxId?: string;
    associated?: {
        title: string,
        type: 'page',
        id: string
    };
}

@Component({
    selector: 'noded-search-modal',
    templateUrl: './Search.component.html',
    styleUrls: ['./Search.component.scss'],
})
export class SearchComponent implements OnInit {
    @ViewChild('ionInput') ionInput: IonInput;
    @Input() query = '';
    public results: BehaviorSubject<SearchResult[]> = new BehaviorSubject<SearchResult[]>([]);
    public allResults: BehaviorSubject<SearchResult[]> = new BehaviorSubject<SearchResult[]>([]);
    public loading = false;

    public typeIcons = NodeTypeIcons;
    public allResultTypes = ['all', 'page', 'node', 'db', 'code', 'files'];
    public resultTypes = ['all'];
    public typeTitles = {
        all: 'All',
        page: 'Pages',
        node: 'Nodes',
        db: 'Databases',
        code: 'Code',
        files: 'Files',
    };

    public selectedType = 'all';

    protected searchChangeDebounce = debounce(async ($event) => {
        const query = $event.detail.value;
        const results = await this.search(query);
        this.allResults.next(results);

        if ( !this.allResults.getValue() ) {
            this.resultTypes = ['all'];
        } else {
            const existentTypes = this.allResults.getValue().map(x => this.classifyResult(x));
            this.resultTypes = this.allResultTypes.filter((x: any) => existentTypes.includes(x) || x === 'all');
        }

        this.filterView(this.selectedType);
        this.loading = false;
    }, 1000);

    constructor(
        protected ionModalController: ModalController,
        protected api: ApiService,
        protected router: Router,
        protected opener: OpenerService,
    ) { }

    async dismiss() {
        await this.ionModalController.dismiss();
    }

    public isDark() {
        return document.body.classList.contains('dark');
    }

    filterView(name: string) {
        this.selectedType = name;
        this.results.next((this.allResults.getValue() || []).filter(x => {
            if ( this.selectedType === 'all' ) {
                return true;
            }

            return this.classifyResult(x) === this.selectedType;
        }));
    }

    classifyResult(result: SearchResult) {
        if ( ['page', 'form'].includes(result.type) ) {
            return 'page';
        } else if ( ['node', 'markdown'].includes(result.type) ) {
            return 'node';
        } else if ( result.type === 'code' ) {
            return 'code';
        } else if ( result.type === 'db' ) {
            return 'db';
        } else if ( ['file_box', 'files'].includes(result.type) || result.type.startsWith('file_box') ) {
            return 'files';
        }
    }

    ngOnInit() {
        setTimeout(() => {
            this.ionInput.setFocus();
        }, 400);
    }

    async onSearchChange($event) {
        this.loading = true;
        this.searchChangeDebounce($event);
    }

    async search(query): Promise<SearchResult[]> {
        return new Promise(resolve => {
            this.api.get(`/search?query=${query}`).subscribe(res => {
                resolve(res.data.results as SearchResult[]);
            });
        });
    }

    async openResult(result: SearchResult) {
        debug('Search result:', result);
        const nodeTypes = [
            'node', 'code', 'files', 'markdown',
        ];

        if ( result.type === 'page' ) {
            await this.opener.openTarget(result.id);
            await this.dismiss();
        } else if ( nodeTypes.includes(result.type) ) {
            await this.opener.openTarget(result.associated.id, result.id);
            await this.dismiss();
        } else if ( result.type === 'db' ) {
            await this.opener.openDatabase(result.associated.id, result.id);
        } else if ( result.type.startsWith('file_box') ) {
            await this.opener.openFileBox(result.associated.id, result.id, result.boxId);
        } else {
            debug('No open handler for search result!');
        }
    }

    async openRelated(event, result: SearchResult) {
        event.preventDefault();
        event.stopPropagation();

        if ( result.associated ) {
            if ( result.associated.type === 'page' ) {
                await this.opener.openTarget(result.associated.id);
                await this.dismiss();
            }
        }
    }
}
