const listAnchor = "_List";
const oldInterfaceLink = "/registered/main.pl?cmd=internalReports&index=yes";

class InternalToolsApp extends React.Component {
    constructor(props) {
        super(props);

        this.setCategories = this.setCategories.bind(this);
        this.setProcessingResult = this.setProcessingResult.bind(this);
        this.setTool = this.setTool.bind(this);
        this.setToolData = this.setToolData.bind(this);
        this.submitToolForm = this.submitToolForm.bind(this);
        this.handleSubmitError = this.handleSubmitError.bind(this);
        this.dropTool = this.dropTool.bind(this);
        this.removeNotification = this.removeNotification.bind(this);

        this.counter = 0;
        this.state = {
            notifications: [],
            toolLabel: null,
            categories: null,
            environment: null,
            tool: null,
            toolProcessingResult: null,
        };

        let anchor = window.top.location.hash;
        if (anchor && anchor.substr(1) !== listAnchor) {
            this.state.toolLabel = anchor.substr(1);
        }
    }

    componentDidMount() {
        if (this.state.toolLabel !== null) {
            this.setTool(this.state.toolLabel);
        }
    }

    componentDidUpdate() {
        if (this.state.tool !== null) {
            document.title = this.state.tool.name;
        } else {
            document.title = "Внутренние инструменты";
        }
        document.querySelectorAll('pre code').forEach((block) => {
            hljs.highlightBlock(block);
        });
    }

    removeNotification(key) {
        this.setState((prevState) => ({
            notifications: prevState.notifications.filter((el) => (el.key !== key)),
            categories: prevState.categories,
            environment: prevState.environment,
            tool: prevState.tool,
            toolLabel: prevState.toolLabel,
            toolProcessingResult: prevState.toolProcessingResult,
        }));
    }

    notify(title, message) {
        console.log(title, message);
        this.setState((prevState) => ({
            notifications: prevState.notifications.concat({title: title, message: message, key: ++this.counter}),
            categories: prevState.categories,
            environment: prevState.environment,
            tool: prevState.tool,
            toolLabel: prevState.toolLabel,
            toolProcessingResult: prevState.toolProcessingResult,
        }));
    }

    setCategories(response) {
        if (window.history && window.history.pushState) {
            this.pushHistoryState(listAnchor);
        }
        this.setState((prevState) => ({
            notifications: prevState.notifications,
            categories: response.result,
            environment: response.environment,
            tool: prevState.tool,
            toolLabel: prevState.toolLabel,
            toolProcessingResult: prevState.toolProcessingResult,
        }));
    }

    setProcessingResult(response) {
        if (this.state.tool) {
            if (response.result !== null && !response.result.message && $.isEmptyObject(response.result.data)) {
                response.result.message = "Сервер вернул пустой результат";
            }

            this.setState((prevState) => ({
                notifications: prevState.notifications,
                categories: prevState.categories,
                environment: prevState.environment,
                tool: prevState.tool,
                toolLabel: prevState.toolLabel,
                toolProcessingResult: response.result,
            }));
        }
    }

    setTool(label, fromHistory) {
        if (!fromHistory) {
            this.pushHistoryState(label);
        }
        this.setState((prevState) => ({
            notifications: [],
            categories: prevState.categories,
            environment: prevState.environment,
            toolLabel: label,
            tool: null,
            toolProcessingResult: null,
        }));
        request(toolGetUrl(label), null, this.setToolData, false, this.handleSubmitError);
    }

    setToolData(response) {
        if (this.state.toolLabel === response.result.label) {
            this.setState((prevState) => ({
                notifications: prevState.notifications,
                categories: prevState.categories,
                toolLabel: prevState.toolLabel,
                tool: response.result,
                environment: response.environment,
                toolProcessingResult: response.result.bareRunData ? response.result.bareRunData : null,
            }));
        }
    }

    uploadFile(label, elementId, callback, errorCallback) {
        let element = $('#' + elementId);
        if (!element.val()) {
            callback(elementId, null);
            return;
        }

        let formData = new FormData();
        formData.append("file", element[0].files[0]);
        request(toolFileUploadUrl(label), formData, function (response) {
                callback(elementId, response.result.key)
            },
            true, errorCallback, true)
    }

    getFileUploadCallback(label, files, data, successCallback, errorCallback) {
        let toProcess = {};
        files.forEach(function (id) {
            console.log("add " + id);
            toProcess[id] = 1;
        });

        let keys = {};
        let requestData = $.extend(true, {}, data);

        return function (elementId, key) {
            console.log("process " + elementId);

            keys[elementId] = key;
            for (let key in toProcess) {
                if (toProcess.hasOwnProperty(key) && !keys.hasOwnProperty(key)) {
                    return;
                }
            }

            for (let key in keys) {
                if (keys.hasOwnProperty(key)) {
                    let name = $('#' + key).attr('name');
                    requestData[name] = keys[key];
                }
            }

            request(toolWriteUrl(label), requestData, successCallback, true, errorCallback);
        }
    }

    splitDescriptionByLine(description) {
        return description.split('\n')
            .map(function (line) {
                return <span>
                    {line}<br/>
                </span>;
            });
    }

    submitToolForm(label, method, data, files, successCallback, errorCallback) {
        this.setProcessingResult({result: {message: "Loading..."}});
        app = this;

        let internalSuccessCallback = function (response) {
            successCallback();
            app.setProcessingResult(response)
        };
        let internalErrorCallback = function (response) {
            errorCallback();
            app.handleSubmitError(response)
        };
        if (method === "POST") {
            if (files && files.length > 0) {
                let callback = this.getFileUploadCallback(label, files, data,
                    internalSuccessCallback, internalErrorCallback);
                files.forEach((elementId) => (this.uploadFile(label, elementId, callback, errorCallback)));
            } else {
                request(toolWriteUrl(label), data,
                    internalSuccessCallback,
                    true,
                    internalErrorCallback);
            }
        } else {
            request(toolReadUrl(label), data,
                internalSuccessCallback,
                false,
                internalErrorCallback);
        }
    }

    handleSubmitError(error) {
        this.setProcessingResult({result: null});

        try {
            let responseJSON = $.parseJSON(error.responseText);
            if (responseJSON.hasOwnProperty('validation_result')) {
                const defects = responseJSON.validation_result.errors;
                for (const key in defects) {
                    if (defects.hasOwnProperty(key)) {
                        let errorText = defects[key].path + " " + defects[key].description + " " + defects[key].value;
                        if (defects[key].params) {
                            errorText += ": " + JSON.stringify(defects[key].params);
                        }
                        notify("Validation error", errorText);
                    }
                }
            } else {
                notify(responseJSON.text, responseJSON.description);
            }
        } catch (e) {
            notify("Unexpected error", e.message);
        }
    }

    dropTool(event, fromHistory) {
        if (!fromHistory) {
            this.pushHistoryState(listAnchor);
        }

        if (event.ctrlKey || event.metaKey) {
            return;
        }
        this.setState((prevState) => ({
            notifications: [],
            categories: prevState.categories,
            environment: prevState.environment,
            toolLabel: null,
            tool: null,
            toolProcessingResult: null,
        }));
    }

    renderLoading() {
        return (
            <div className="row">
                <div className="col-sm-12">
                    <p className="lead">Loading...</p>
                </div>
                {this.renderNotifications()}
            </div>
        );
    }

    renderCategories() {
        if (this.state.categories === null) {
            request(categoriesListUrl(), null, this.setCategories, false, this.handleSubmitError);
            return this.renderLoading();
        }
        return (
            <div>
                <div className="row">
                    <div className="col-sm-12">
                        <h1>Внутренние инструменты</h1>
                        <h4>Конфигурация БД: {this.state.environment}</h4>
                        <p>
                            <small><a href={oldInterfaceLink}>(Старый интерфейс)</a></small>
                        </p>
                    </div>
                </div>
                {this.renderNotifications()}
                <CategoriesList items={this.state.categories} onSetTool={this.setTool}/>
            </div>
        );
    }

    renderToolBlock() {
        return (
            <div>
                <div className="row">
                    <div className="col-sm-12">
                        <a href={"#" + listAnchor} onClick={this.dropTool}>Назад к списку инструментов</a>
                    </div>
                </div>
                <div className="row">
                    <div className="col-sm-12">
                        <h1>{this.state.tool.name}</h1>
                        <h4>Конфигурация БД: {this.state.environment}</h4>
                        <p className="it-description-text">
                            {this.splitDescriptionByLine(this.state.tool.description)}
                        </p>
                        {this.renderDisclaimers()}
                    </div>
                </div>
                <Form data={this.state.tool} onSubmit={this.submitToolForm}/>
                {this.renderNotifications()}
                {this.renderProcessingResult()}
            </div>
        );
    }

    renderDisclaimers() {
        let disclaimers = this.state.tool.disclaimers;
        if (!disclaimers || disclaimers.length === 0) {
            return '';
        }
        return (<ul>
            {disclaimers.map((d) => (<li><span className="text-warning">{d}</span></li>))}
        </ul>);
    }

    renderProcessingResult() {
        if (this.state.toolProcessingResult === null) {
            return '';
        }

        return <InternalToolResult message={this.state.toolProcessingResult.message}
                                   data={this.state.toolProcessingResult.data}
                                   details={this.state.toolProcessingResult.details}/>;
    }

    renderNotifications() {
        return <NotificationsContainer items={this.state.notifications} onDismiss={this.removeNotification}/>
    }

    render() {
        if (this.state.tool !== null) {
            return this.renderToolBlock();
        } else if (this.state.toolLabel !== null) {
            return this.renderLoading();
        } else {
            return this.renderCategories();
        }
    }

    pushHistoryState(label) {
        history.pushState({label: label}, label, "#" + label);
    }
}

function renderApp() {
    return ReactDOM.render(
        <InternalToolsApp/>,
        document.getElementById("app")
    );
}

let app = renderApp();

function notify(text, message) {
    app.notify(text, message);
}

window.onpopstate = function (event) {
    if (window.history && window.history.state) {
        if (event.state !== null) {
            if (event.state.label !== listAnchor) {
                app.setTool(event.state.label, true);
            } else {
                app.dropTool(event, true);
            }
        }
    } else {
        window.history.back();
    }
};
