import { dispatch as d3_dispatch } from 'd3-dispatch';
import { select as d3_select } from 'd3-selection';

import { t } from '../core/localizer';
import { services } from '../services';
import { modeBrowse } from '../modes/browse';
import { svgIcon } from '../svg/icon';

// import { uiField } from './field';
// import { uiFormFields } from './form_fields';

import { uiNoteComments } from './note_comments';
import { uiNoteHeader } from './note_header';
import { uiNoteReport } from './note_report';
import { uiViewOnOSM } from './view_on_osm';

import {
    utilNoAuto,
    utilRebind
} from '../util';
import { osmNote } from '../osm';


export function uiNoteEditor(context) {
    var dispatch = d3_dispatch('change');
    var noteComments = uiNoteComments(context);
    var noteHeader = uiNoteHeader();

    // var formFields = uiFormFields(context);

    var _note;
    var _newNote;
    // var _fieldsArr;


    function noteEditor(selection) {

        var header = selection.selectAll('.header')
            .data([0]);

        var headerEnter = header.enter()
            .append('div')
            .attr('class', 'header fillL');

        headerEnter
            .append('button')
            .attr('class', 'close')
            .attr('title', t('icons.close'))
            .on('click', function() {
                context.enter(modeBrowse(context));
            })
            .call(svgIcon('#iD-icon-close'));

        headerEnter
            .append('h2')
            .call(t.append('note.title'));


        var body = selection.selectAll('.body')
            .data([0]);

        body = body.enter()
            .append('div')
            .attr('class', 'body')
            .merge(body);

        var editor = body.selectAll('.note-editor')
            .data([0]);

        editor.enter()
            .append('div')
            .attr('class', 'modal-section note-editor')
            .merge(editor)
            .call(noteHeader.note(_note))
            .call(noteComments.note(_note))
            .call(noteSaveSection);

        var footer = selection.selectAll('.footer')
            .data([0]);

        footer.enter()
            .append('div')
            .attr('class', 'footer')
            .merge(footer)
            .call(uiViewOnOSM(context).what(_note))
            .call(uiNoteReport(context).note(_note));


        // rerender the note editor on any auth change
        var osm = services.osm;
        if (osm) {
            osm.on('change.note-save', function() {
                selection.call(noteEditor);
            });
        }
    }


    function noteSaveSection(selection) {
        var isSelected = (_note && _note.id === context.selectedNoteID());
        var noteSave = selection.selectAll('.note-save')
            .data((isSelected ? [_note].filter(d => d.status !== 'hidden') : []), d => d.status + d.id);

        // exit
        noteSave.exit()
            .remove();

        // enter
        var noteSaveEnter = noteSave.enter()
            .append('div')
            .attr('class', 'note-save save-section cf');

        // // if new note, show categories to pick from
        // if (_note.isNew()) {
        //     var presets = presetManager;

        //     // NOTE: this key isn't a age and therefore there is no documentation (yet)
        //     _fieldsArr = [
        //         uiField(context, presets.field('category'), null, { show: true, revert: false }),
        //     ];

        //     _fieldsArr.forEach(function(field) {
        //         field
        //             .on('change', changeCategory);
        //     });

        //     noteSaveEnter
        //         .append('div')
        //         .attr('class', 'note-category')
        //         .call(formFields.fieldsArr(_fieldsArr));
        // }

        // function changeCategory() {
        //     // NOTE: perhaps there is a better way to get value
        //     var val = context.container().select('input[name=\'category\']:checked').property('__data__') || undefined;

        //     // store the unsaved category with the note itself
        //     _note = _note.update({ newCategory: val });
        //     var osm = services.osm;
        //     if (osm) {
        //         osm.replaceNote(_note);  // update note cache
        //     }
        //     noteSave
        //         .call(noteSaveButtons);
        // }

        noteSaveEnter
            .append('h4')
            .attr('class', '.note-save-header')
            .text('')
            .each(function() {
                if (_note.isNew()) {
                    t.append('note.newDescription')(d3_select(this));
                } else {
                    t.append('note.newComment')(d3_select(this));
                }
            });

        var commentTextarea = noteSaveEnter
            .append('textarea')
            .attr('class', 'new-comment-input')
            .attr('placeholder', t('note.inputPlaceholder'))
            .attr('maxlength', 1000)
            .property('value', function(d) { return d.newComment; })
            .call(utilNoAuto)
            .on('keydown.note-input', keydown)
            .on('input.note-input', changeInput)
            .on('blur.note-input', changeInput);

        if (!commentTextarea.empty() && _newNote) {
            // autofocus the comment field for new notes
            commentTextarea.node().focus();
        }

        // update
        noteSave = noteSaveEnter
            .merge(noteSave)
            .call(userDetails)
            .call(noteSaveButtons);


        // fast submit if user presses cmd+enter
        function keydown(d3_event) {
            if (!(d3_event.keyCode === 13 && // ↩ Return
                d3_event.metaKey)) return;

            var osm = services.osm;
            if (!osm) return;

            var hasAuth = osm.authenticated();
            if (!hasAuth) return;

            if (!_note.newComment) return;

            d3_event.preventDefault();

            d3_select(this)
                .on('keydown.note-input', null);

            // focus on button and submit
            window.setTimeout(function() {
                if (_note.isNew()) {
                    noteSave.selectAll('.save-button').node().focus();
                    clickSave(_note);
                } else  {
                    noteSave.selectAll('.comment-button').node().focus();
                    postCommentAndStatus(_note);
                }
            }, 10);
        }


        function changeInput() {
            var input = d3_select(this);
            var val = input.property('value').trim() || undefined;

            // store the unsaved comment with the note itself
            _note = _note.update({ newComment: val });

            var osm = services.osm;
            if (osm) {
                osm.replaceNote(_note);  // update note cache
            }

            noteSave
                .call(noteSaveButtons);
        }
    }


    function userDetails(selection) {
        var detailSection = selection.selectAll('.detail-section')
            .data([0]);

        detailSection = detailSection.enter()
            .append('div')
            .attr('class', 'detail-section')
            .merge(detailSection);

        var osm = services.osm;
        if (!osm) return;

        // Add warning if user is not logged in
        var hasAuth = osm.authenticated();
        var authWarning = detailSection.selectAll('.auth-warning')
            .data(hasAuth ? [] : [0]);

        authWarning.exit()
            .transition()
            .duration(200)
            .style('opacity', 0)
            .remove();

        var authEnter = authWarning.enter()
            .insert('div', '.tag-reference-body')
            .attr('class', 'field-warning auth-warning')
            .style('opacity', 0);

        authEnter
            .call(svgIcon('#iD-icon-alert', 'inline'));

        authEnter
            .append('span')
            .call(t.append('note.login'));

        authEnter
            .append('a')
            .attr('target', '_blank')
            .call(svgIcon('#iD-icon-out-link', 'inline'))
            .append('span')
            .call(t.append('login'))
            .on('click.note-login', function(d3_event) {
                d3_event.preventDefault();
                osm.authenticate();
            });

        authEnter
            .transition()
            .duration(200)
            .style('opacity', 1);


        var prose = detailSection.selectAll('.note-save-prose')
            .data(hasAuth ? [0] : []);

        prose.exit()
            .remove();

        prose = prose.enter()
            .append('p')
            .attr('class', 'note-save-prose')
            .call(t.append('note.upload_explanation'))
            .merge(prose);

        osm.userDetails(function(err, user) {
            if (err) return;

            const userLink = selection => {
                if (user.image_url) {
                    selection
                        .append('img')
                        .attr('src', user.image_url)
                        .attr('class', 'icon pre-text user-icon');
                }

                selection
                    .append('a')
                    .attr('class', 'user-info')
                    .text(user.display_name)
                    .attr('href', osm.userURL(user.display_name))
                    .attr('target', '_blank');
            };

            prose
                .call(t.addOrUpdate('note.upload_explanation_with_user', { user: userLink }));
        });
    }


    function noteSaveButtons(selection) {
        var osm = services.osm;
        var hasAuth = osm && osm.authenticated();

        var isSelected = (_note && _note.id === context.selectedNoteID());
        var buttonSection = selection.selectAll('.buttons')
            .data((isSelected ? [_note] : []), d => d.status + d.id);

        // exit
        buttonSection.exit()
            .remove();

        // enter
        var buttonEnter = buttonSection.enter()
            .append('div')
            .attr('class', 'buttons');

        if (_note.isNew()) {
            buttonEnter
                .append('button')
                .attr('class', 'button cancel-button secondary-action')
                .call(t.append('confirm.cancel'));

            buttonEnter
                .append('button')
                .attr('class', 'button save-button action')
                .call(t.append('note.save'));

        } else {
            buttonEnter
                .append('button')
                .attr('class', 'button status-button action');

            buttonEnter
                .append('button')
                .attr('class', 'button comment-button action')
                .call(t.append('note.comment'));
        }


        // update
        buttonSection = buttonSection
            .merge(buttonEnter);

        buttonSection.select('.cancel-button')   // select and propagate data
            .on('click.cancel', clickCancel);

        buttonSection.select('.save-button')     // select and propagate data
            .attr('disabled', isSaveDisabled)
            .on('click.save', clickSave);

        buttonSection.select('.status-button')   // select and propagate data
            .attr('disabled', (hasAuth ? null : true))
            .each(function(d) {
                var action = (d.status === 'open' ? 'close' : 'open');
                var andComment = (d.newComment ? '_comment' : '');
                t.addOrUpdate('note.' + action + andComment)(d3_select(this));
            })
            .on('click.status', function(d3_event, note) {
                const setStatus = (note.status === 'open' ? 'closed' : 'open');
                postCommentAndStatus.bind(this)(d3_event, note, setStatus);
            });

        buttonSection.select('.comment-button')   // select and propagate data
            .attr('disabled', isSaveDisabled)
            .on('click.comment', postCommentAndStatus);


        function isSaveDisabled(d) {
            return (hasAuth && d.status === 'open' && d.newComment) ? null : true;
        }
    }



    function clickCancel(d3_event, d) {
        this.blur();    // avoid keeping focus on the button - #4641
        var osm = services.osm;
        if (osm) {
            osm.removeNote(d);
        }
        context.enter(modeBrowse(context));
        dispatch.call('change', d);
    }


    function clickSave(d3_event, note) {
        this.blur();    // avoid keeping focus on the button - #4641
        var osm = services.osm;
        if (osm) {
            osm.postNoteCreate(note, (err, d) => dispatch.call('change', d));
        }
    }


    function postCommentAndStatus(d3_event, note, newStatus) {
        this.blur();    // avoid keeping focus on the button - #4641
        var osm = services.osm;
        if (osm) {
            osm.postNoteUpdate(note, newStatus || note.status, function(err, d) {
                if (!err) {
                    dispatch.call('change', d);
                } else if (err.status === 409) {
                    // note was probably closed in the meantime: reload it - #8464
                    osm.loadEntityNote(note.id, (err, d) => {
                        dispatch.call('change', osmNote({ ...d.data[0], newComment: note.newComment }));
                    });
                } else if (err.status === 410) {
                    // note was deleted/hidden by a moderator
                    osm.removeNote(note);
                    dispatch.call('change', osmNote({ id: note.id, status: 'hidden', comments: [...note.comments, { action: 'hidden' }] }));
                }
            });
        }
    }


    noteEditor.note = function(val) {
        if (!arguments.length) return _note;
        _note = val;
        return noteEditor;
    };

    noteEditor.newNote = function(val) {
        if (!arguments.length) return _newNote;
        _newNote = val;
        return noteEditor;
    };


    return utilRebind(noteEditor, dispatch, 'on');
}
