import $ from 'jquery';
import Component from '@ember/component';
import classic from 'ember-classic-decorator';
import {
    IMAGE_EXTENSIONS,
    IMAGE_MIME_TYPES
} from 'ghost-admin/components/gh-image-uploader';
import {action, computed, set, setProperties} from '@ember/object';
import {utils as ghostHelperUtils} from '@tryghost/helpers';
import {isEmpty} from '@ember/utils';
import {run} from '@ember/runloop';
import {inject as service} from '@ember/service';

const {countWords} = ghostHelperUtils;

@classic
export default class KoenigCardImage extends Component {
    @service ui;

    // attrs
    editor = null;
    files = null;
    payload = null;
    isSelected = false;
    isEditing = false;
    imageExtensions = IMAGE_EXTENSIONS;
    imageMimeTypes = IMAGE_MIME_TYPES;

    // properties
    handlesDragDrop = true;
    isEditingAlt = false;

    // closure actions
    selectCard() {}
    deselectCard() {}
    editCard() {}
    saveCard() {}
    deleteCard() {}
    moveCursorToNextSection() {}
    moveCursorToPrevSection() {}
    addParagraphAfterCard() {}
    registerComponent() {}

    @computed('payload.src')
    get isEmpty() {
        return !this.payload.src;
    }

    @computed('payload.imageSelector')
    get imageSelector() {
        let selector = this.payload.imageSelector;
        let imageSelectors = {
            unsplash: {
                component: 'gh-unsplash',
                type: 'modal'
            },
            tenor: {
                component: 'koenig-card-image/selector-tenor',
                type: 'placeholder'
            }
        };

        return imageSelectors[selector];
    }

    @computed('payload.{src,caption}')
    get counts() {
        let wordCount = 0;
        let imageCount = 0;

        if (this.payload.src) {
            imageCount += 1;
        }

        if (this.payload.caption) {
            wordCount += countWords(this.payload.caption);
        }

        return {wordCount, imageCount};
    }

    @computed('payload.cardWidth')
    get kgImgStyle() {
        let cardWidth = this.payload.cardWidth;

        if (cardWidth === 'wide') {
            return 'image-wide';
        }

        if (cardWidth === 'full') {
            return 'image-full';
        }

        return 'image-normal';
    }

    @computed('isEditingLink', 'payload.{cardWidth,src}')
    get toolbar() {
        if (!this.payload.src || this.isEditingLink) {
            return false;
        }

        let cardWidth = this.payload.cardWidth;
        let toolbarItems;

        if (this.payload.type === 'gif') {
            toolbarItems = [{
                title: 'Link',
                icon: 'koenig/kg-link',
                iconClass: this.payload.href ? 'fill-green-l2' : 'fill-white',
                action: run.bind(this, this._editLink)
            }];
        } else {
            toolbarItems = [{
                title: 'Regular',
                icon: 'koenig/kg-img-regular',
                iconClass: !cardWidth ? 'fill-green-l2' : 'fill-white',
                action: run.bind(this, this._changeCardWidth, '')
            }, {
                title: 'Wide',
                icon: 'koenig/kg-img-wide',
                iconClass: cardWidth === 'wide' ? 'fill-green-l2' : 'fill-white',
                action: run.bind(this, this._changeCardWidth, 'wide')
            }, {
                title: 'Full',
                icon: 'koenig/kg-img-full',
                iconClass: cardWidth === 'full' ? 'fill-green-l2' : 'fill-white',
                action: run.bind(this, this._changeCardWidth, 'full')
            }, {
                divider: true
            }, {
                title: 'Link',
                icon: 'koenig/kg-link',
                iconClass: this.payload.href ? 'fill-green-l2' : 'fill-white',
                action: run.bind(this, this._editLink)
            }, {
                title: 'Replace image',
                icon: 'koenig/kg-replace',
                iconClass: 'fill-white',
                action: run.bind(this, this._triggerFileDialog)
            }];
        }

        return {items: toolbarItems};
    }

    init() {
        super.init(...arguments);

        if (!this.payload) {
            this.set('payload', {});
        }

        let placeholders = ['summer', 'mountains', 'ufo-attack'];
        this.set('placeholder', placeholders[Math.floor(Math.random() * placeholders.length)]);

        this.registerComponent(this);
    }

    didReceiveAttrs() {
        super.didReceiveAttrs(...arguments);

        // `payload.files` can be set if we have an externaly set image that
        // should be uploaded. Typical example would be from a paste or drag/drop
        if (!isEmpty(this.payload.files)) {
            run.schedule('afterRender', this, function () {
                this.set('files', this.payload.files);

                // we don't want to  persist any file data in the document
                delete this.payload.files;
            });
        }

        // switch back to displaying caption when card is not selected
        if (!this.isSelected) {
            this.set('isEditingAlt', false);
        }
    }

    didInsertElement() {
        super.didInsertElement(...arguments);

        if (this.payload.triggerBrowse && !this.payload.src && !this.payload.files) {
            // we don't want to persist this in the serialized payload
            this._updatePayloadAttr('triggerBrowse', undefined);

            let fileInput = this.element.querySelector('input[type="file"]');
            if (fileInput) {
                fileInput.click();
            }
        }

        if (this.imageSelector) {
            this.scrollToCard();
        }
    }

    @action
    updateSrc(images) {
        let [image] = images;

        // create undo snapshot when image finishes uploading
        this.editor.run(() => {
            this._updatePayloadAttr('src', image.url);
            if (this._imageWidth && this._imageHeight) {
                this._updatePayloadAttr('width', this._imageWidth);
                this._updatePayloadAttr('height', this._imageHeight);
            }
            this._imageWidth = null;
            this._imageHeight = null;
        });
    }

    /**
     * Opens a file selection dialog - Triggered by "Upload Image" buttons,
     * searches for the hidden file input within the .gh-setting element
     * containing the clicked button then simulates a click
     * @param  {MouseEvent} event - MouseEvent fired by the button click
     */
    @action
    triggerFileDialog(event) {
        this._triggerFileDialog(event);
    }

    @action
    setPreviewSrc(files) {
        let file = files[0];
        if (file) {
            let url = URL.createObjectURL(file);
            this.set('previewSrc', url);

            let imageElem = new Image();
            imageElem.onload = () => {
                // store width/height for use later to avoid saving an image card with no `src`
                this._imageWidth = imageElem.naturalWidth;
                this._imageHeight = imageElem.naturalHeight;
            };
            imageElem.src = url;
        }
    }

    @action
    resetSrcs() {
        this.set('previewSrc', null);
        this._imageWidth = null;
        this._imageHeight = null;

        // create undo snapshot when clearing
        this.editor.run(() => {
            this._updatePayloadAttr('src', null);
            this._updatePayloadAttr('width', null);
            this._updatePayloadAttr('height', null);
        });
    }

    @action
    selectFromImageSelector({src, width, height, caption, alt, type}) {
        let {payload, saveCard} = this;
        let searchTerm;

        setProperties(payload, {src, width, height, caption, alt, type, searchTerm});

        this.send('closeImageSelector');

        // create undo snapshot when selecting an image
        this.editor.run(() => {
            saveCard(payload, false);
        });
        this.deselectCard();
        this.selectCard();
        this.scrollToCard();
    }

    @action
    closeImageSelector(reselectParagraph = true) {
        if (!this.payload.src) {
            if (!this.env.postModel.parent) {
                // card has been deleted by cleanup
                return;
            }

            return this.editor.run((postEditor) => {
                let {builder} = postEditor;
                let cardSection = this.env.postModel;
                let p = builder.createMarkupSection('p');

                postEditor.replaceSection(cardSection, p);

                if (reselectParagraph) {
                    postEditor.setRange(p.tailPosition());
                }
            });
        }

        set(this.payload, 'imageSelector', undefined);
    }

    @action
    updateHref(href) {
        this._updatePayloadAttr('href', href);
    }

    @action
    cancelEditLink() {
        this.set('isEditing', false);
        this.set('isEditingLink', false);
    }

    @action
    onDeselect() {
        if (this.imageSelector?.type === 'placeholder' && !this.payload.src) {
            this.send('closeImageSelector', false);
        }
    }

    @action
    updateCaption(caption) {
        this._updatePayloadAttr('caption', caption);
    }

    @action
    toggleAltEditing() {
        this.toggleProperty('isEditingAlt');
    }

    @action
    updateAlt(alt) {
        this._updatePayloadAttr('alt', alt);
    }

    dragOver(event) {
        if (!event.dataTransfer) {
            return;
        }

        // this is needed to work around inconsistencies with dropping files
        // from Chrome's downloads bar
        if (navigator.userAgent.indexOf('Chrome') > -1) {
            let eA = event.dataTransfer.effectAllowed;
            event.dataTransfer.dropEffect = (eA === 'move' || eA === 'linkMove') ? 'move' : 'copy';
        }

        event.stopPropagation();
        event.preventDefault();

        this.set('isDraggedOver', true);
    }

    dragLeave(event) {
        event.preventDefault();
        this.set('isDraggedOver', false);
    }

    drop(event) {
        event.preventDefault();
        this.set('isDraggedOver', false);

        if (event.dataTransfer.files) {
            this.set('files', [event.dataTransfer.files[0]]);
        }
    }

    _changeCardWidth(cardWidth) {
        // create undo snapshot when changing image size
        this.editor.run(() => {
            this._updatePayloadAttr('cardWidth', cardWidth);
        });
    }

    _updatePayloadAttr(attr, value) {
        let payload = this.payload;
        let save = this.saveCard;

        set(payload, attr, value);

        // update the mobiledoc and stay in edit mode
        save(payload, false);
    }

    _triggerFileDialog(event) {
        let target = event && event.target || this.element;

        // simulate click to open file dialog
        // using jQuery because IE11 doesn't support MouseEvent
        $(target)
            .closest('.__mobiledoc-card')
            .find('input[type="file"]')
            .click();
    }

    _editLink(event) {
        event.preventDefault();
        event.stopPropagation();
        const rect = this.element.getBoundingClientRect();
        rect.x = rect.x - 5;
        this.set('linkRect', rect);
        this.set('isEditing', true); // hide snippet icon in toolbar
        this.set('isEditingLink', true);
    }
}
