/**
 * Created by victor on 07/04/21.
 */
import React, {Component} from 'react';

import sanitizeHtml from "sanitize-html";

import {parse, stringify} from 'himalaya';
import _ from 'underscore';
import findParentForm from '../../helpers/findParentForm';

let sanitizeConf = {
    allowedTags: [
        "address", "article", "aside", "footer", "header", "h1", "h2", "h3", "h4",
        "h5", "h6", "hgroup", "main", "nav", "section", "blockquote", "dd", "div",
        "dl", "dt", "figcaption", "figure", "hr", "li", "main", "ol", "p", "pre",
        "ul", "a", "abbr", "b", "bdi", "bdo", "br", "cite", "code", "data", "dfn",
        "em", "i", "kbd", "mark", "q", "rb", "rp", "rt", "rtc", "ruby", "s", "samp",
        "small", "span", "strong", "sub", "sup", "time", "u", "var", "wbr", "caption",
        "col", "colgroup", "table", "tbody", "td", "tfoot", "th", "thead", "tr", "font",
        "img"
    ],
    disallowedTagsMode: 'discard',
    allowedAttributes: {
        a: ['href', 'name', 'target'],
        img: ['src', 'alt'],
        '*': ['href', 'align', 'alt', 'center', 'style', 'color']
    },
    selfClosing: ['img', 'br', 'hr', 'area', 'base', 'basefont', 'input', 'link', 'meta'],
    allowedSchemes: ['http', 'https', 'ftp', 'mailto', 'tel'],
    allowedSchemesByTag: {
        img: ['data']
    },
    allowedSchemesAppliedToAttributes: ['href', 'src', 'cite'],
    allowProtocolRelative: true,
    enforceHtmlBoundary: false
}


import "./style/editor.scss";

import IconH1 from './icons/type-h1.svg?jsx';
import IconH2 from './icons/type-h2.svg?jsx';
import IconH3 from './icons/type-h3.svg?jsx';
import IconBold from './icons/type-bold.svg?jsx';
import IconItalic from './icons/type-italic.svg?jsx';
import IconStrike from './icons/type-strikethrough.svg?jsx';
import IconUnderline from './icons/type-underline.svg?jsx';
import IconLink from './icons/link.svg?jsx';
import IconOL from './icons/list-ol.svg?jsx';
import IconUL from './icons/list-ul.svg?jsx';
import IconTextLeft from './icons/text-left.svg?jsx';
import IconTextCenter from './icons/text-center.svg?jsx';
import IconTextRight from './icons/text-right.svg?jsx';
import IconFontColor from './icons/font.svg?jsx';
import IconBrash from './icons/brush-fill.svg?jsx';
import IconTable from './icons/grid-3x3.svg?jsx';

import Confirm from '../../components/Confirm/Confirm';

import Emoji from './elements/Emoji/Emoji'

import palette from './pallette.json';

import cn from 'classnames';
import Dropdown from "sbx-react-dropdown";
import {conformToMask} from "react-text-mask";
import store from "../../store";
import {getValidator, validations} from "../../helpers/helpers";
import shortid from "shortid";

function rgbToRgba(color, alpha) {
    if (color) {
        return color.replace('rgb', 'rgba').slice(0, -1) + ', ' + alpha + ')';
    }
    return ''
}

class TableSelect extends React.Component{
    static defaultProps = {
        grid: [10, 10]
    }
    state = {
        hover: null,
    }
    render(){
        let {hover} = this.state;
        let {onSelect, grid} = this.props;

        const [countRows, countCols] = grid;

        let cols = [];
        for(let i = 1; i <= countRows * countCols; i++){
            let key = 'col-' + i;
            let hoverRow = Math.ceil(hover / countCols);
            let row = Math.ceil(i / countCols);

            let hoverCol = hover - (hoverRow - 1) * countCols;
            let col = i - (row - 1) * countCols;


            cols.push(
                <div
                    key={key}
                    onMouseEnter={() => this.setState({hover: i})}
                    onMouseLeave={() => this.setState({hover: null})}
                    className={cn({
                        'editorTableSelect__item': true,
                        'editorTableSelect__item_hover': row <= hoverRow && col <= hoverCol,
                    })}
                    onClick={e => {
                        e.preventDefault();
                        let rows = [];
                        let td = [];
                        for(let k = 0; k < col; k++){
                            td.push(`<td><br/></td>`);
                        }
                        for(let i = 0; i < row; i++){
                            rows.push(
                                `<tr>${td.join('')}</tr>`
                            );
                        }
                        let tbody = `<tbody>${rows.join('')}</tbody>`;
                        onSelect && onSelect(`<table>${tbody}</table>`);
                    }}
                >

                </div>
            )
        }

        let row = 0;
        let col = 0;
        if(hover !== null){
            row = Math.ceil(hover / countCols);
            col = hover - (row - 1) * countCols;
        }

        return <div className="editorTableSelect">
            <div
                className="editorTableSelect__grid"
                onMouseLeave={() => this.setState({hover: null})}
                style={{
                    gridTemplateColumns: `repeat(${countCols}, auto)`
                }}
            >
                {cols}
            </div>
            <div className="editorTableSelect__label">
                {row} x {col}
            </div>
        </div>
    }
}

@findParentForm
export default class Editor extends React.Component {

    id = shortid();

    get _formElement() {
        return true;
    }

    static defaultProps = {
        controls: {
            h1: true,
            h2: true,
            h3: true,
            link: true,
            table: true,
            bold: true,
            italic: true,
            underline: true,
            strikeThrough: true,
            insertUnorderedList: true,
            insertOrderedList: true,
            justifyLeft: true,
            justifyCenter: true,
            justifyRight: true,
            color: true,
            backColor: true,
        }
    }

    state = {
        showConfirmUrl: null,
        selection: null,
        focus: null,
        commandsEnabled: {},
        value: null,
    }

    get raw() {
        let raw = parse(this.value);
        raw.push({
            type: 'textPlain',
            content: this.main.innerText
        })
        return raw;
    }

    get value() {
        return sanitizeHtml(this.main.innerHTML, sanitizeConf);
    }

    clear() {
        this.main.innerHTML = '';
        this.setState({value: ''});
        if (this.props.onChange)
            this.props.onChange(this.value, this.raw);
        this.validate();
    }

    execCommand = (cmd, arg) => {
        this.main.focus();
        setTimeout(() => {
            document.execCommand(cmd, false, arg);
        }, 0);
        this.checkCommands();
    }

    checkCommands = () => {
        setTimeout(() => {
            this.setState({
                commandsEnabled: {
                    bold: document.queryCommandState("bold"),
                    formatblock: document.queryCommandValue("formatblock"),
                    italic: document.queryCommandState("italic"),
                    strikeThrough: document.queryCommandState("strikeThrough"),
                    insertUnorderedList: document.queryCommandState("insertUnorderedList"),
                    insertOrderedList: document.queryCommandState("insertOrderedList"),
                    justifyLeft: document.queryCommandState("justifyLeft"),
                    justifyCenter: document.queryCommandState("justifyCenter"),
                    justifyRight: document.queryCommandState("justifyRight"),
                    underline: document.queryCommandState("underline"),
                    foreColor: document.queryCommandValue("foreColor"),
                    backColor: document.queryCommandValue("backColor"),
                }
            })
        }, 0)
    }

    __setRaw() {
        if (this.props.raw) {
            let raw = this.props.raw;
            if (raw[raw.length - 1]?.type === 'textPlain') {
                raw.pop();
            }
            this.main.innerHTML = sanitizeHtml(stringify(raw), sanitizeConf);
            this.setState({value: this.value});
        }
    }

    __onChange = (e) => {
        this.setState({value: this.value}, () => {
            if (this.props.onChange)
                this.props.onChange(this.value, this.raw);
            this.validate();
        });
    };

    __onFocus(e) {
        if(this.props.onFocus){
            this.props.onFocus(e);
        }
        this.setState({focus: true})
    }

    __onBlur = (e) => {
        if(this.props.onBlur){
            this.props.onBlur(e);
        }
        this.setState({focus: false});
        this.validate();
    };

    componentDidMount() {
        this.main.focus();
        this.checkCommands();
        // document.execCommand('foreColor', false, '#000');
        this.__setRaw();
        this.main.blur();
    }

    componentDidUpdate(prevProps, prevState, snapshot) {
        if (prevProps.value !== this.props.value) {
            this.main.innerHTML = sanitizeHtml(this.props.value, sanitizeConf);
            this.setState({value: this.value});
        }
        if ((!prevProps.raw || !prevProps.raw.length) && !_.isEqual(prevProps.raw, this.props.raw)) {
            this.__setRaw();
        }
    }

    get input(){
        return this.main
    }

    get _value() {
        return this.raw;
    }

    set(value) {

    }
    validate() {
        let {_value: value} = this;
        let {validate, parentid, name} = this.props;
        let promises = [];
        let values = {};
        for(let field in store[parentid]){
            values[field] = store[parentid][field]._value;
        }
        if (!validate) {
            this.setState({success: true, error: null});
            return Promise.resolve(value);
        } else if ({}.toString.call(validate) === '[object Function]') {
            promises.push(validate(value));
        } else {
            for (let key in validate) {
                let valid = null;
                let error = validate[key].error || 'ERROR';
                if (validate[key].custom) {
                    valid = validate[key].custom(value, values);
                } else {
                    let validator = getValidator(key);
                    valid = validations[validator.name](value, ...validator.args);
                }
                if (valid) {
                    promises.push(Promise.resolve())
                } else {
                    promises.push(Promise.reject(error));
                    break;
                }
            }
        }
        return Promise.all(promises).then(() => {
            this.setState({success: true, error: null});
            return Promise.resolve(value);
        }).catch((error) => {
            this.setState({success: null, error});
            return Promise.reject(error);
        })
    }


    render() {
        let {showConfirmUrl, commandsEnabled, focus, value, success, error} = this.state;
        let {placeholder, children, label, controls} = this.props;

        let controlsEnabled = {
            h1: true,
            h2: true,
            h3: true,
            bold: true,
            italic: true,
            link: true,
            table: true,
            underline: true,
            strikeThrough: true,
            insertUnorderedList: true,
            insertOrderedList: true,
            justifyLeft: true,
            justifyCenter: true,
            justifyRight: true,
            color: true,
            backColor: true,
            ...controls
        }

        return <div className="sbxFormField">
            {label && <label
                className="sbxFormField__label"
            >
                {label}
            </label>}
            <div className={cn({
                'sbxFormField__input': true,
                'editor': true,
                'editor_focus': focus,
                'editor_valid': success,
                'editor_invalid': error,
            })}>
                <Confirm
                    mode="prime"
                    visible={showConfirmUrl}
                    comment
                    title="Please enter a link"
                    onRequestClose={() => {
                        this.setState({showConfirmUrl: null, selection: null})
                    }}
                    onConfirm={href => {
                        let {range} = this.state;

                        this.setState({showConfirmUrl: null, selection: null}, () => {
                            document.getSelection().removeAllRanges();
                            document.getSelection().addRange(range);
                            document.execCommand('createLink', false, href)
                        })
                    }}
                />
                {controls && <div
                    className="editor__controls"
                >
                    {controlsEnabled.h1 && <a
                        onMouseDown={e => {
                            e.preventDefault();
                            this.execCommand('formatblock', 'h1')
                        }}
                        className={cn({
                            'active': commandsEnabled.formatblock === 'h1'
                        })}
                    >
                        <IconH1/>
                    </a>}
                    {controlsEnabled.h2 && <a
                        onMouseDown={e => {
                            e.preventDefault();
                            this.execCommand('formatblock', 'h2')
                        }}
                        className={cn({
                            'active': commandsEnabled.formatblock === 'h2'
                        })}
                    >
                        <IconH2/>
                    </a>}
                    {controlsEnabled.h3 && <a
                        onMouseDown={e => {
                            e.preventDefault();
                            this.execCommand('formatblock', 'h3')
                        }}
                        className={cn({
                            'active': commandsEnabled.formatblock === 'h3'
                        })}
                    >
                        <IconH3/>
                    </a>}
                    {controlsEnabled.bold && <a
                        onMouseDown={e => {
                            e.preventDefault();
                            this.execCommand('bold');
                        }}
                        className={cn({
                            'active': commandsEnabled.bold
                        })}
                    >
                        <IconBold/>
                    </a>}
                    {controlsEnabled.italic && <a
                        onMouseDown={e => {
                            e.preventDefault();
                            this.execCommand('italic');
                        }}
                        className={cn({
                            'active': commandsEnabled.italic
                        })}
                    >
                        <IconItalic/>
                    </a>}
                    {controlsEnabled.underline && <a
                        onMouseDown={e => {
                            e.preventDefault();
                            this.execCommand('underline');
                        }}
                        className={cn({
                            'active': commandsEnabled.underline
                        })}
                    >
                        <IconUnderline/>
                    </a>}
                    {controlsEnabled.strikeThrough && <a
                        onMouseDown={e => {
                            e.preventDefault();
                            this.execCommand('strikeThrough');
                        }}
                        className={cn({
                            'active': commandsEnabled.strikeThrough
                        })}
                    >
                        <IconStrike/>
                    </a>}
                    {controlsEnabled.insertUnorderedList && <a
                        onMouseDown={e => {
                            e.preventDefault();
                            this.execCommand('insertUnorderedList');
                        }}
                        className={cn({
                            'active': commandsEnabled.insertUnorderedList
                        })}
                    >
                        <IconUL/>
                    </a>}
                    {controlsEnabled.insertOrderedList && <a
                        onMouseDown={e => {
                            e.preventDefault();
                            this.execCommand('insertOrderedList');
                            this.main.focus();
                        }}
                        className={cn({
                            'active': commandsEnabled.insertOrderedList
                        })}
                    >
                        <IconOL/>
                    </a>}
                    {controlsEnabled.link && <a
                        onMouseDown={e => {
                            e.preventDefault();
                            let selection = document.getSelection();
                            if (selection.rangeCount > 0) {
                                if (selection.anchorNode && selection.anchorNode.parentNode.tagName === 'A') {
                                    document.execCommand('unlink', false)
                                } else {
                                    this.setState({
                                        showConfirmUrl: true,
                                        range: selection.getRangeAt(0).cloneRange()
                                    })
                                }
                            }
                        }}
                    >
                        <IconLink/>
                    </a>}
                    {controlsEnabled.justifyLeft && <a
                        onMouseDown={e => {
                            e.preventDefault();
                            this.execCommand('justifyLeft');
                        }}
                        className={cn({
                            'active': commandsEnabled.justifyLeft
                        })}
                    >
                        <IconTextLeft/>
                    </a>}
                    {controlsEnabled.justifyCenter && <a
                        onMouseDown={e => {
                            e.preventDefault();
                            this.execCommand('justifyCenter');
                        }}
                        className={cn({
                            'active': commandsEnabled.justifyCenter
                        })}
                    >
                        <IconTextCenter/>
                    </a>}
                    {controlsEnabled.justifyRight && <a
                        onMouseDown={e => {
                            e.preventDefault();
                            this.execCommand('justifyRight');
                        }}
                        className={cn({
                            'active': commandsEnabled.justifyRight
                        })}
                    >
                        <IconTextRight/>
                    </a>}
                    {controlsEnabled.table && <Dropdown
                        ref={e => this.dropdownTableSelect = e}
                        triangle={false}
                        position="bottom_left"
                        data={<TableSelect
                            onSelect={value => {
                                let {range} = this.state;
                                if(range){
                                    document.getSelection().removeAllRanges();
                                    document.getSelection().addRange(range);
                                }
                                this.execCommand('insertHTML', value);
                                this.setState({range: null})
                                this.dropdownTableSelect.hide();
                            }}
                        />}
                    >
                        <a
                            onMouseDown={e => {
                                e.preventDefault();
                                this.setState({
                                    range: document.getSelection().getRangeAt(0).cloneRange()
                                })
                            }}
                        >
                            <IconTable/>
                        </a>
                    </Dropdown>}
                    {/*<Dropdown*/}
                    {/*    ref={e => this.dropdownEmoji = e}*/}
                    {/*    data={<Emoji*/}
                    {/*        onSelect={emoji => {*/}
                    {/*            let {range} = this.state;*/}
                    {/*            if (range) {*/}
                    {/*                document.getSelection().removeAllRanges();*/}
                    {/*                document.getSelection().addRange(range);*/}
                    {/*            }*/}
                    {/*            this.execCommand('insertHTML', emoji);*/}
                    {/*            this.setState({range: null})*/}
                    {/*        }}*/}
                    {/*    />}*/}
                    {/*    triangle={false}*/}
                    {/*    position="bottom_left"*/}
                    {/*>*/}
                    {/*    <a*/}
                    {/*        onMouseDown={e => {*/}
                    {/*            e.preventDefault();*/}
                    {/*            let selection = document.getSelection();*/}
                    {/*            if (selection.rangeCount > 0) {*/}
                    {/*                this.setState({*/}
                    {/*                    range: selection.getRangeAt(0).cloneRange()*/}
                    {/*                })*/}
                    {/*            }*/}
                    {/*        }}*/}
                    {/*    >*/}
                    {/*        <IconEmoji/>*/}
                    {/*    </a>*/}
                    {/*</Dropdown>*/}
                    <div className="editor__headerRight">
                        {controlsEnabled.color && <Dropdown
                            ref={e => this.dropdownBackColor = e}
                            triangle={false}
                            position="bottom_right"
                            data={<div>
                                <button
                                    className="editor__resetBgButton"
                                    onClick={e => {
                                        e.preventDefault();
                                        let {range} = this.state;
                                        if (range) {
                                            document.getSelection().removeAllRanges();
                                            document.getSelection().addRange(range);
                                        }

                                        this.execCommand('backColor', 'transparent');
                                        this.setState({range: null})
                                        this.dropdownBackColor.hide();
                                    }}
                                >
                                    Reset
                                </button>
                                <div className="editor__colors">
                                    {palette.map((row, index) => {
                                        return <div
                                            key={'row-' + index}
                                            className="editor__colorsRow"
                                        >
                                            {row.map((color) => {
                                                return <div
                                                    key={color + index + '-color'}
                                                    style={{backgroundColor: color}}
                                                    className={cn({
                                                        'editor__colorsRowItem': true,
                                                        'editor__colorsRowItem_active': false,
                                                    })}
                                                    onClick={e => {
                                                        let {range} = this.state;
                                                        if (range) {
                                                            document.getSelection().removeAllRanges();
                                                            document.getSelection().addRange(range);
                                                        }

                                                        this.execCommand('backColor', color);
                                                        this.setState({range: null})
                                                        this.dropdownBackColor.hide();
                                                    }}
                                                >

                                                </div>
                                            })}
                                        </div>
                                    })}
                                </div>
                            </div>}
                        >
                            <a
                                onMouseDown={e => {
                                    e.preventDefault();
                                    let selection = document.getSelection();
                                    if (selection.rangeCount > 0) {
                                        this.setState({
                                            range: selection.getRangeAt(0).cloneRange()
                                        })
                                    }
                                }}
                                className="editor__colorBtn"
                            >
                                <IconBrash/>
                                <div
                                    className="editor__colorBtnValue"
                                    style={{
                                        backgroundColor: commandsEnabled.backColor
                                    }}
                                >

                                </div>
                            </a>
                        </Dropdown>}
                        {controlsEnabled.backColor && <Dropdown
                            ref={e => this.dropdownForColor = e}
                            triangle={false}
                            position="bottom_right"
                            data={<div className="editor__colors">
                                {palette.map((row, index) => {
                                    return <div
                                        key={'row-' + index}
                                        className="editor__colorsRow"
                                    >
                                        {row.map((color) => {
                                            return <div
                                                key={color + index + '-color'}
                                                style={{backgroundColor: color}}
                                                className={cn({
                                                    'editor__colorsRowItem': true,
                                                    'editor__colorsRowItem_active': false,
                                                })}
                                                onClick={e => {
                                                    let {range} = this.state;
                                                    if (range) {
                                                        document.getSelection().removeAllRanges();
                                                        document.getSelection().addRange(range);
                                                    }

                                                    this.execCommand('foreColor', color);
                                                    this.setState({range: null})
                                                    this.dropdownForColor.hide();
                                                }}
                                            >

                                            </div>
                                        })}
                                    </div>
                                })}
                            </div>}
                        >
                            <a
                                onMouseDown={e => {
                                    e.preventDefault();
                                    let selection = document.getSelection();
                                    if (selection.rangeCount > 0) {
                                        this.setState({
                                            range: selection.getRangeAt(0).cloneRange()
                                        })
                                    }
                                }}
                                className="editor__colorBtn"
                            >
                                <IconFontColor/>
                                <div
                                    className="editor__colorBtnValue"
                                    style={{
                                        backgroundColor: commandsEnabled.foreColor
                                    }}
                                >

                                </div>
                            </a>
                        </Dropdown>}
                    </div>
                </div>}
                <div className="editor__mainWrap">
                    {(!value && placeholder) && <div className="editor__mainPlaceholder">
                        {placeholder}
                    </div>}
                    <div
                        ref={e => this.main = e}
                        className="editor__main"
                        contentEditable
                        onFocus={() => this.__onFocus()}
                        onBlur={() => this.__onBlur()}
                        onKeyDown={() => {
                            this.checkCommands()
                        }}
                        onInput={e => {
                            let value = e.currentTarget.innerHTML;
                            if (!value || value === '<br>') {
                                this.clear();
                            }
                            if (this.state.value !== this.value) {
                                this.__onChange();
                            }
                        }}
                        onMouseUp={e => {
                            this.checkCommands();
                        }}
                    >

                    </div>
                </div>
                {children && <div className="editor__footer">
                    {children}
                </div>}
            </div>
            {error && <div
                className='sbxFormField__invalidFeedback'
            >
                {error}
            </div>}
        </div>
    }
}
