﻿import * as ko from "knockout";
import * as Utils from "@paperbits/common/utils";
import template from "./media.html";
import { IMediaService } from "@paperbits/common/media";
import { ViewManager, View } from "@paperbits/common/ui";
import { MediaItem, defaultFileName, defaultURL } from "./mediaItem";
import { MediaContract } from "@paperbits/common/media/mediaContract";
import { EventManager, Events } from "@paperbits/common/events";
import { Component, OnMounted } from "@paperbits/common/ko/decorators";
import { ChangeRateLimit } from "@paperbits/common/ko/consts";
import { Query, Operator, Page } from "@paperbits/common/persistence";
import { MimeTypes } from "@paperbits/common";
import { UserError } from "@paperbits/common/errors";

@Component({
    selector: "media",
    template: template
})
export class MediaWorkshop {
    private currentPage: Page<MediaContract>;
    public readonly searchPattern: ko.Observable<string>;
    public readonly mediaItems: ko.ObservableArray<MediaItem>;
    public readonly selectedMediaItem: ko.Observable<MediaItem>;
    public readonly working: ko.Observable<boolean>;

    constructor(
        private readonly eventManager: EventManager,
        private readonly mediaService: IMediaService,
        private readonly viewManager: ViewManager
    ) {
        this.working = ko.observable(false);
        this.mediaItems = ko.observableArray<MediaItem>();
        this.searchPattern = ko.observable<string>("");
        this.selectedMediaItem = ko.observable<MediaItem>();
    }

    @OnMounted()
    public async initialize(): Promise<void> {
        await this.searchMedia();

        this.searchPattern
            .extend(ChangeRateLimit)
            .subscribe(this.searchMedia);
    }

    public async searchMedia(searchPattern: string = ""): Promise<void> {
        this.working(true);
        this.mediaItems([]);

        const query = Query
            .from<MediaContract>()
            .orderBy(`fileName`);

        if (searchPattern) {
            query.where(`fileName`, Operator.contains, searchPattern);
        }

        const mediaOfResults = await this.mediaService.search(query);
        this.currentPage = mediaOfResults;

        const mediaItems = mediaOfResults.value.map(media => new MediaItem(media));
        this.mediaItems.push(...mediaItems);

        this.working(false);
    }

    public async loadNextPage(): Promise<void> {
        if (!this.currentPage?.takeNext || this.working()) {
            return;
        }

        this.working(true);

        this.currentPage = await this.currentPage.takeNext();

        const mediaItems = this.currentPage.value.map(page => new MediaItem(page));
        this.mediaItems.push(...mediaItems);

        this.working(false);
    }

    public async uploadMedia(): Promise<void> {
        this.eventManager.dispatchEvent(Events.HintRequest, {
            key: "88d9",
            content: `You may upload pictures, videos and other files just by dropping them anywhere in editor window and even make respective widget out of them.`
        });

        const files = await this.viewManager.openUploadDialog();

        this.working(true);

        const uploadPromises = [];

        try {
            for (const file of files) {
                const content = await Utils.readFileAsByteArray(file);
                const uploadPromise = this.mediaService.createMedia(file.name, content, file.type);

                this.viewManager.notifyProgress(uploadPromise, "Media library", `Uploading ${file.name}...`);
                uploadPromises.push(uploadPromise);
            }

            await Promise.all<MediaContract>(uploadPromises);
            await this.searchMedia();
        }
        catch (error) {
            if (error instanceof UserError) {
                this.viewManager.notifyError("Media library", error.message);
            }
            else {
                throw error;
            }
        }
        finally {
            this.working(false);
        }
    }

    public async linkMedia(): Promise<void> {
        const mediaContract = await this.mediaService.createMediaUrl(defaultFileName, defaultURL, MimeTypes.imageSvg);
        const mediaItem = new MediaItem(mediaContract);

        this.mediaItems.push(mediaItem);
        this.selectMedia(mediaItem);
    }

    public selectMedia(mediaItem: MediaItem): void {
        this.selectedMediaItem(mediaItem);

        const view: View = {
            heading: "Media file",
            component: {
                name: "media-details-workshop",
                params: {
                    mediaItem: mediaItem,
                    onDeleteCallback: () => {
                        this.searchMedia();
                    }
                }
            }
        };

        this.viewManager.openViewAsWorkshop(view);
    }

    public async deleteSelectedMedia(): Promise<void> {
        // TODO: Show confirmation dialog according to mockup
        this.viewManager.closeWorkshop("media-details-workshop");

        await this.mediaService.deleteMedia(this.selectedMediaItem().toMedia());
        await this.searchMedia();
    }

    // public onDragStart(item: MediaItem): HTMLElement {
    //     item.widgetFactoryResult = item.widgetOrder.createWidget();

    //     const widgetElement = item.widgetFactoryResult.element;
    //     const widgetModel = item.widgetFactoryResult.widgetModel;
    //     const widgetBinding = item.widgetFactoryResult.widgetBinding;

    //     this.viewManager.beginDrag({
    //         sourceModel: widgetModel,
    //         sourceBinding: widgetBinding
    //     });

    //     return widgetElement;
    // }

    // public onDragEnd(item: MediaItem): void {
    //     item.widgetFactoryResult.element.remove();
    //     const dragSession = this.viewManager.getDragSession();
    //     const acceptorBinding = dragSession.targetBinding;

    //     if (acceptorBinding && acceptorBinding.handler) {
    //         const widgetHandler = this.widgetService.getWidgetHandler(acceptorBinding);
    //         widgetHandler.onDragDrop(dragSession);
    //     }

    //     this.eventManager.dispatchEvent(Events.VirtualDragEnd);
    // }

    public isSelected(media: MediaItem): boolean {
        const selectedMedia = this.selectedMediaItem();
        return selectedMedia?.key === media.key;
    }
}