// (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, Output, OnInit, EventEmitter, ViewChild, ElementRef } from '@angular/core';
import { FileEntry } from '@ionic-native/file/ngx';

import { CoreIonLoadingElement } from '@classes/ion-loading';
import { CoreFile } from '@services/file';
import { CoreFileHelper } from '@services/file-helper';
import { CoreSites } from '@services/sites';
import { CoreDomUtils } from '@services/utils/dom';
import { CoreMimetypeUtils } from '@services/utils/mimetype';
import { CoreTextUtils } from '@services/utils/text';
import { CoreTimeUtils } from '@services/utils/time';
import { CoreUtils } from '@services/utils/utils';
import { CoreForms } from '@singletons/form';

/**
 * Component to handle a local file. Only files inside the app folder can be managed.
 *
 * Shows the file name, icon (depending on extension), size and time modified.
 * Also, if managing is enabled it will also show buttons to rename and delete the file.
 */
@Component({
    selector: 'core-local-file',
    templateUrl: 'core-local-file.html',
})
export class CoreLocalFileComponent implements OnInit {

    @Input() file?: FileEntry; // A fileEntry retrieved using CoreFileProvider.getFile or similar.
    @Input() manage?: boolean | string; // Whether the user can manage the file (edit and delete).
    @Input() overrideClick?: boolean | string; // Whether the default item click should be overridden.
    @Output() onDelete = new EventEmitter<void>(); // Will notify when the file is deleted.
    @Output() onRename = new EventEmitter<{ file: FileEntry }>(); // Will notify when the file is renamed.
    @Output() onClick = new EventEmitter<void>(); // Will notify when the file is clicked. Only if overrideClick is true.

    @ViewChild('nameForm') formElement?: ElementRef;

    fileName?: string;
    fileIcon?: string;
    fileExtension?: string;
    size?: string;
    timemodified?: string;
    newFileName = '';
    editMode = false;
    relativePath?: string;

    /**
     * Component being initialized.
     */
    async ngOnInit(): Promise<void> {
        this.manage = CoreUtils.isTrueOrOne(this.manage);

        if (!this.file) {
            return;
        }

        this.loadFileBasicData(this.file);

        // Get the size and timemodified.
        const metadata = await CoreFile.getMetadata(this.file);
        if (metadata.size >= 0) {
            this.size = CoreTextUtils.bytesToSize(metadata.size, 2);
        }

        this.timemodified = CoreTimeUtils.userDate(metadata.modificationTime.getTime(), 'core.strftimedatetimeshort');

    }

    /**
     * Load the basic data for the file.
     */
    protected loadFileBasicData(file: FileEntry): void {
        this.fileName = file.name;
        this.fileIcon = CoreMimetypeUtils.getFileIcon(file.name);
        this.fileExtension = CoreMimetypeUtils.getFileExtension(file.name);

        // Let's calculate the relative path for the file.
        this.relativePath = CoreFile.removeBasePath(file.toURL());
        if (!this.relativePath) {
            // Didn't find basePath, use fullPath but if the user tries to manage the file it'll probably fail.
            this.relativePath = file.fullPath;
        }
    }

    /**
     * File clicked.
     *
     * @param e Click event.
     */
    async fileClicked(e: Event): Promise<void> {
        if (this.editMode) {
            return;
        }

        e.preventDefault();
        e.stopPropagation();

        if (CoreUtils.isTrueOrOne(this.overrideClick) && this.onClick.observers.length) {
            this.onClick.emit();

            return;
        }

        if (!CoreFileHelper.isOpenableInApp(this.file!)) {
            try {
                await CoreFileHelper.showConfirmOpenUnsupportedFile();
            } catch (error) {
                return; // Cancelled, stop.
            }
        }

        CoreUtils.openFile(this.file!.toURL());
    }

    /**
     * Activate the edit mode.
     *
     * @param e Click event.
     */
    activateEdit(e: Event): void {
        e.preventDefault();
        e.stopPropagation();

        this.editMode = true;
        this.newFileName = this.file!.name;
    }

    /**
     * Rename the file.
     *
     * @param newName New name.
     * @param e Click event.
     */
    async changeName(newName: string, e: Event): Promise<void> {
        e.preventDefault();
        e.stopPropagation();

        if (newName == this.file!.name) {
            // Name hasn't changed, stop.
            this.editMode = false;
            CoreForms.triggerFormCancelledEvent(this.formElement, CoreSites.getCurrentSiteId());

            return;
        }

        const modal = await CoreDomUtils.showModalLoading();
        const fileAndDir = CoreFile.getFileAndDirectoryFromPath(this.relativePath!);
        const newPath = CoreTextUtils.concatenatePaths(fileAndDir.directory, newName);

        try {
            // Check if there's a file with this name.
            await CoreFile.getFile(newPath);

            // There's a file with this name, show error and stop.
            CoreDomUtils.showErrorModal('core.errorfileexistssamename', true);
        } catch {
            try {
                // File doesn't exist, move it.
                const fileEntry = await CoreFile.moveFile(this.relativePath!, newPath);

                CoreForms.triggerFormSubmittedEvent(this.formElement, false, CoreSites.getCurrentSiteId());

                this.editMode = false;
                this.file = fileEntry;
                this.loadFileBasicData(this.file);
                this.onRename.emit({ file: this.file });
            } catch (error) {
                CoreDomUtils.showErrorModalDefault(error, 'core.errorrenamefile', true);
            }
        } finally {
            modal.dismiss();
        }
    }

    /**
     * Delete the file.
     *
     * @param e Click event.
     */
    async deleteFile(e: Event): Promise<void> {
        e.preventDefault();
        e.stopPropagation();

        let modal: CoreIonLoadingElement | undefined;

        try {
            // Ask confirmation.
            await CoreDomUtils.showDeleteConfirm('core.confirmdeletefile');

            modal = await CoreDomUtils.showModalLoading('core.deleting', true);

            await CoreFile.removeFile(this.relativePath!);

            this.onDelete.emit();
        } catch (error) {
            CoreDomUtils.showErrorModalDefault(error, 'core.errordeletefile', true);
        } finally {
            modal?.dismiss();
        }
    }

}
