import {Component, ElementRef, Inject, Input, OnInit, ViewChild} from '@angular/core';
import {ApiService} from '../../../service/api.service';
import {APP_BASE_HREF} from '@angular/common';
import {EditorService} from '../../../service/editor.service';
import {EditorNodeContract} from '../EditorNode.contract';
import {HttpClient} from '@angular/common/http';
import {AlertController, LoadingController, ModalController, PopoverController} from '@ionic/angular';
import {OptionMenuComponent} from '../../option-menu/option-menu.component';
import {NavigationService} from '../../../service/navigation.service';
import {FileBoxPageComponent} from './file-box-page.component';

export interface FileBoxFile {
    type: 'file';
    title: string;
    mime: string;
    uploaded: string;
    id: string;
    category: string;
}

export interface FileBoxRecord {
    type: 'folder';
    title: string;
    UUID: string;
    name: string;
    pageId: string;
    fileIds: string[];
    rootUUID: string;
    parentUUID?: string;
}

export type FileBoxItem = FileBoxFile | FileBoxRecord;

@Component({
    selector: 'editor-file-box',
    templateUrl: './file-box.component.html',
    styleUrls: ['./file-box.component.scss'],
})
export class FileBoxComponent extends EditorNodeContract implements OnInit {
    @Input() nodeId: string;
    @Input() editorUUID: string;
    @Input() fullPage = false;
    @ViewChild('fileInput') fileInput: ElementRef;

    categoryIcons = {
        document: 'fa fa-file-word',
        spreadsheet: 'fa fa-file-excel',
        presentation: 'fa fa-file-powerpoint',
        image: 'fa fa-file-image',
        pdf: 'fa fa-file-pdf',
        video: 'fa fa-file-video',
        code: 'fa fa-file-code',
        text: 'fa fa-file-alt',
        other: 'fa fa-file',
    };

    protected dirty = false;
    protected pendingSetup = true;
    public isLoading = true;
    public notAvailableOffline = false;
    public fileBoxName = 'New File Box';
    public record?: FileBoxRecord;
    private deferredUIActivation = false;
    public quickFilterValue?: string;
    public visibleFilterItems?: FileBoxItem[];
    public history: FileBoxRecord[] = [];
    public items: FileBoxItem[] = [];

    public get readonly() {
        return !this.node || !this.editorService.canEdit();
    }

    constructor(
        protected api: ApiService,
        protected http: HttpClient,
        public editorService: EditorService,
        protected loading: LoadingController,
        protected popover: PopoverController,
        protected alerts: AlertController,
        protected navService: NavigationService,
        protected modals: ModalController,
        @Inject(APP_BASE_HREF) private baseHref: string
    ) { super(); }

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

    public isDirty(): boolean | Promise<boolean> {
        return this.dirty;
    }

    public writeChangesToNode(): void | Promise<void> {
        this.node.Value.Mode = 'files';
    }

    public needsLoad(): boolean | Promise<boolean> {
        return this.node && this.pendingSetup;
    }

    public async performLoad(): Promise<void> {
        if ( !this.node.Value ) {
            this.node.Value = {};
        }

        if ( this.api.isOffline ) {
            this.notAvailableOffline = true;
            this.pendingSetup = false;
            return;
        }

        if ( !this.node.Value.Value && !this.readonly ) {
            this.record = await this.api.createFileBox(this.page.UUID, this.node.UUID, this.fileBoxName);
            this.node.Value.Value = this.record.UUID;
            this.node.value = this.record.UUID;
            this.dirty = true;
        } else if ( this.node.Value.Value ) {
            this.record = await this.api.getFileBox(this.page.UUID, this.node.UUID, this.node.Value.Value);
        }

        if ( !this.record ) {
            this.notAvailableOffline = true;
            this.pendingSetup = false;
            return;
        }

        this.fileBoxName = this.record.name;

        await this.loadBox();

        if ( this.dirty ) {
            this.editorService.triggerSave();
        }

        if ( this.fileInput ) {
            this.fileInput.nativeElement.value = null;
        }

        this.pendingSetup = false;

        if ( this.deferredUIActivation ) {
            await this.performUIActivation();
        }
    }

    public async loadBox(): Promise<void> {
        this.isLoading = true;
        this.fileBoxName = this.record.name;
        const files = await this.api.getFileBoxFiles(this.page.UUID, this.node.UUID, this.record.UUID);
        const children = await this.api.getFileBoxChildren(this.page.UUID, this.node.UUID, this.record.UUID);

        this.items = [...children, ...files];
        this.refilter();
        this.isLoading = false;
    }

    public async navigateUp() {
        if ( this.history.length < 1 ) {
            return;
        }

        const last = this.history[this.history.length - 1];
        if ( last ) {
            await this.navigateBack(last);
        }
    }

    public async navigateBack(record: FileBoxRecord) {
        const newHistory: FileBoxRecord[] = [];

        const found = this.history.some(row => {
            if ( row.UUID === record.UUID ) {
                return true;
            } else {
                newHistory.push(row);
            }
        });

        if ( found ) {
            this.history = newHistory;
        } else {
            this.history = [];
        }

        this.record = record;
        await this.loadBox();
    }

    public async performDelete(): Promise<void> {
        const baseRecord = this.history.length > 0 ? this.history[0] : this.record;
        await this.api.deleteFileBox(this.page.UUID, this.node.UUID, baseRecord.UUID);
    }

    ngOnInit() {
        this.editorService = this.editorService.getEditor(this.editorUUID);
        this.editorService.registerNodeEditor(this.nodeId, this).then(() => {

        });
    }

    surfaceContextItems() {
        return [
            {
                name: 'New Folder',
                icon: 'fa fa-folder-plus',
                value: 'folder-add',
                title: 'Create a new folder in the current file box',
            },
        ];
    }

    async onSurfaceContextMenu(event: MouseEvent) {
        if ( this.readonly || this.isLoading ) {
            return;
        }

        if ( !event.ctrlKey ) {
            event.preventDefault();
            event.stopPropagation();

            const popover = await this.popover.create({
                event,
                component: OptionMenuComponent,
                componentProps: {
                    menuItems: [
                        ...this.surfaceContextItems(),
                    ],
                },
            });

            popover.onDidDismiss().then(result => {
                if ( result.data ) {
                    this.onActionClick(result.data);
                }
            });

            await popover.present();
        }
    }

    itemContextItems(item: FileBoxItem) {
        return [
            {
                name: 'Rename',
                value: 'rename',
                icon: 'fa fa-edit',
                title: 'Rename this ' + item.type,
            },
            {
                name: 'Delete',
                value: 'delete',
                icon: 'fa fa-trash',
                title: 'Delete this ' + item.type,
            },
        ];
    }

    async onRecordNameChange(event) {
        if ( this.readonly ) {
            return;
        }

        const name = event.target.value;
        this.fileBoxName = name;
        this.record.name = name;
        this.record.title = name;
        await this.api.updateFileBox(this.page.UUID, this.node.UUID, this.record.UUID, { name });
        this.navService.requestSidebarRefresh({ quiet: true });
    }

    async onItemContextMenu(item: FileBoxItem, event: MouseEvent) {
        if ( this.readonly || this.isLoading ) {
            return;
        }

        if ( !event.ctrlKey ) {
            event.preventDefault();
            event.stopPropagation();

            const popover = await this.popover.create({
                event,
                component: OptionMenuComponent,
                componentProps: {
                    menuItems: [
                        ...this.itemContextItems(item),
                        ...this.surfaceContextItems(),
                    ],
                },
            });

            popover.onDidDismiss().then(result => {
                if ( result.data ) {
                    this.onActionClick(result.data, item);
                }
            });

            await popover.present();
        }
    }

    async onUploadFilesClick(event) {
        if ( this.readonly ) {
            return;
        }

        if ( this.fileInput ) {
            this.fileInput.nativeElement.click();
        }
    }

    async onUploadFilesChange(event) {
        if ( this.readonly ) {
            return;
        }

        const loader = await this.loading.create({
            message: 'Uploading files...',
        });

        await loader.present();

        const fileList: FileList = this.fileInput?.nativeElement?.files;
        if ( !fileList ) {
            return;
        }

        if ( fileList.length > 0 ) {
            const formData: FormData = new FormData();

            // tslint:disable-next-line:prefer-for-of
            for ( let i = 0; i < fileList.length; i += 1 ) {
                const file = fileList[i];
                formData.append(`uploaded_file_${i}`, file, file.name);
            }

            await this.api.uploadFileBoxFiles(this.page.UUID, this.node.UUID, this.record.UUID, formData);
            await this.loadBox();
        }

        await loader.dismiss();
    }

    async onItemActivate(item: FileBoxItem) {
        if ( item.type === 'folder' ) {
            this.history.push(this.record);
            this.record = item;
            await this.loadBox();
        } else if ( item.type === 'file' ) {
            const url = this.api.getFileBoxFileDownloadUrl(this.page.UUID, this.node.UUID, this.record.UUID, item.id);
            window.open(url, '_blank');
        }
    }

    async onActionClick(action: string, item?: FileBoxItem) {
        if ( this.readonly ) {
            return;
        }

        if ( action === 'folder-add' ) {
            await this.actionNewFolder();
        } else if ( action === 'rename' && item ) {
            await this.actionRename(item);
        } else if ( action === 'delete' && item ) {
            await this.actionDelete(item);
        }
    }

    async actionNewFolder() {
        const alert = await this.alerts.create({
            header: 'New Folder',
            message: 'Enter a name for the new folder:',
            inputs: [
                {
                    name: 'name',
                    placeholder: 'New Folder',
                },
            ],
            buttons: [
                {
                    text: 'Create',
                    role: 'ok',
                },
                {
                    text: 'Cancel',
                    role: 'cancel',
                },
            ],
        });

        alert.onDidDismiss().then(async result => {
            if ( result.role === 'ok' ) {
                const name = result.data?.values?.name?.trim() || 'New Folder';
                await this.api.createFileBox(this.page.UUID, this.node.UUID, name, this.record.rootUUID, this.record.UUID);
                await this.loadBox();
            }
        });

        await alert.present();
    }

    async actionRename(item: FileBoxItem) {
        const alert = await this.alerts.create({
            header: 'Rename ' + item.type,
            message: `Enter a new name for the ${item.type}:`,
            inputs: [
                {
                    name: 'name',
                    value: item.title,
                },
            ],
            buttons: [
                {
                    text: 'Rename',
                    role: 'ok',
                },
                {
                    text: 'Cancel',
                    role: 'cancel',
                },
            ],
        });

        alert.onDidDismiss().then(async result => {
            const name = result.data?.values?.name?.trim();
            if ( result.role === 'ok' && name ) {
                if ( item.type === 'folder' ) {
                    item.name = name;
                    item.title = name;
                    await this.api.updateFileBox(this.page.UUID, this.node.UUID, item.UUID, { name });
                } else if ( item.type === 'file' ) {
                    item.title = name;
                    await this.api.updateFileBoxFile(this.page.UUID, this.node.UUID, this.record.UUID, item.id, { name });
                }
            }
        });

        await alert.present();
    }

    async actionDelete(item: FileBoxItem) {
        const alert = await this.alerts.create({
            header: `Delete ${item.type}?`,
            message: `Are you sure you want to delete the ${item.type} "${item.title}"? This action cannot be undone.`,
            buttons: [
                {
                    text: 'Delete It',
                    role: 'ok',
                },
                {
                    text: 'Keep It',
                    role: 'cancel',
                },
            ],
        });

        alert.onDidDismiss().then(async result => {
            if ( result.role === 'ok' ) {
                if ( item.type === 'file' ) {
                    await this.api.deleteFileBoxFile(this.page.UUID, this.node.UUID, this.record.UUID, item.id);
                    await this.loadBox();
                } else if ( item.type === 'folder' ) {
                    await this.api.deleteFileBox(this.page.UUID, this.node.UUID, item.UUID);
                    await this.loadBox();
                }
            }
        });

        await alert.present();
    }

    onQuickFilterChange(event) {
        this.quickFilterValue = event.detail.value || undefined;
        this.refilter();
    }

    refilter() {
        if ( this.quickFilterValue ) {
            let visible = [...this.items];
            const parts = this.quickFilterValue.toLowerCase().split(/\s/);

            for ( const part of parts ) {
                visible = visible.filter(item => item.title.toLowerCase().includes(part));
            }

            this.visibleFilterItems = visible;
        } else {
            this.visibleFilterItems = undefined;
        }
    }

    async openFileBox() {
        const modal = await this.modals.create({
            component: FileBoxPageComponent,
            componentProps: {
                nodeId: this.nodeId,
                pageId: this.editorService.currentPageId,
            },
            cssClass: 'modal-big',
        });

        modal.onDidDismiss().then(() => {
            this.record = this.history.length > 0 ? this.history[0] : this.record;
            this.history = [];
            this.loadBox();
        });

        const modalState = {
            modal : true,
            desc : 'Open File Box'
        };

        history.pushState(modalState, null);

        await modal.present();
    }

    async dismiss() {
        if ( this.fullPage ) {
            await this.modals.dismiss();
        }
    }

    performUIActivation() {
        if ( this.deferredUIActivation ) {
            this.deferredUIActivation = false;
        }

        if ( !this.pendingSetup ) {
            return this.openFileBox();
        } else {
            this.deferredUIActivation = true;
        }
    }
}
