import React from 'react';
import {withContextConsumer} from "@formanta/react";
import {ApiRequest, GET, POST} from "../../../lib/ApiRequest";
import {cancelPromise} from "../../../component/cancelPromise";
import {withApiControl} from "../../../component/ApiControl";
import {isUndefined} from "@formanta/js";
import {withPusher, Pushing} from "../../../component/Pusher/Pusher";

const StoreMediaContext = React.createContext({});

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

        this.state = {
            folderRoot: {},
            folders: {},
            loadingFolder: {},
            loadingFolderRoot: false,
            loadFolder: this.loadFolder,
            createDir: this.createDir,
            isFolderLoaded: this.isFolderLoaded
        };
    }

    /**
     *
     * @param {undefined|string} folder
     * @return {boolean|string}
     */
    isFolderLoaded = (folder) => {
        if(folder) {
            if(isUndefined(this.state.loadingFolder[folder])) {
                return false;
            }
            return this.state.loadingFolder[folder];
        }

        return this.state.loadingFolderRoot;
    };

    /**
     * @param {undefined|string} folder
     * @param {boolean|string} status
     */
    updateFolderLoaded = (folder, status) => {
        if(folder) {
            const loadingFolder = {...this.state.loadingFolder};
            loadingFolder[folder] = status;
            this.setState({loadingFolder});
        } else {
            this.setState({loadingFolderRoot: status});
        }
    };

    loadFolder = (folder = undefined, forceLoad = false) => {
        if(false === this.isFolderLoaded(folder) || forceLoad) {
            const {cancelPromise} = this.props;
            this.updateFolderLoaded(folder, 'progress');

            cancelPromise(new ApiRequest('file', GET, 'files/list' + (folder ? '/' + encodeURIComponent(folder) : ''))
                .debug(true)
                .exec())
                .then((res) => {
                    if(res && res.data) {
                        if(res.data.dir) {
                            res.data.dir = res.data.dir.map((elem, i) => {
                                elem.delete = (folder,) => {
                                    this.deleteDir(folder, elem.name, i);
                                };

                                elem.folder = folder;
                                elem.deleting = false;

                                elem.rename = (folder, to) => {
                                    this.renameDir(folder, elem.name, to, i);
                                };

                                elem.renaming = false;

                                return elem;
                            });
                        }
                        if(res.data.file) {
                            res.data.file = res.data.file.map((elem, i) => {
                                elem.delete = (folder,) => {
                                    this.deleteFile(folder, elem.name, i);
                                };

                                elem.folder = folder;
                                elem.deleting = false;

                                elem.rename = (folder, to) => {
                                    this.renameFile(folder, elem.name, to, i);
                                };

                                elem.renaming = false;

                                return elem;
                            });
                        }
                        if(folder) {
                            const folders = {...this.state.folders};
                            folders[folder] = res.data;

                            this.setState({folders});
                        } else {
                            this.setState({folderRoot: res.data});
                        }
                        this.updateFolderLoaded(folder, true);
                    } else {
                        this.updateFolderLoaded(folder, 'error');
                    }
                })
                .catch((err) => {
                    this.props.handleApiFail(err, 'listing Folder');
                    if(err && !err.cancelled) {
                        this.updateFolderLoaded(folder, 'error');
                    }
                });
        }
    };

    updateFileState = (folder, index, evt) => {
        let list = {};
        if(folder) {
            const folders = {...this.state.folders};
            list = {...folders[folder]};
        } else {
            list = {...this.state.folderRoot};
        }
        const files = [...list.file];
        let file = {...files[index]};
        file = evt(file);

        file.delete = (folder,) => {
            this.deleteFile(folder, file.name, index);
        };

        file.rename = (folder, to) => {
            this.renameFile(folder, file.name, to, index);
        };

        files[index] = file;
        list.file = files;

        if(folder) {
            this.setState({
                folders: list
            });
        } else {
            this.setState({
                folderRoot: list
            });
        }
    };

    deleteFile = (folder, file, index) => {
        const {execPushing, cancelPromise} = this.props;
        cancelPromise(execPushing(
            new Pushing(
                'deleteFile',
                'deleteFile',
                (resolve, reject) => {
                    this.updateFileState(folder, index, (file) => {
                        file.deleting = 'progress';
                        return file;
                    });

                    (new ApiRequest('file', POST, 'files/delete-file/' + (folder ? encodeURIComponent(folder + '/' + file) : encodeURIComponent('/' + file))))
                        .debug(true)
                        .header({})
                        .exec()
                        .then((res) => {
                            if(res && res.data) {
                                resolve(res.data);
                            } else {
                                reject();
                            }
                        })
                        .catch(() => {
                            reject();
                        });
                }
            )
        ))
            .then((data) => {
                if(data.error) {
                    this.updateFileState(folder, index, (file) => {
                        file.deleting = 'error';
                        return file;
                    });
                    return;
                }

                this.updateFileState(folder, index, (file) => {
                    file.deleting = 'success';
                    return file;
                });

                setTimeout(() => {
                    this.updateFileState(folder, index, (file) => {
                        file.deleting = 'finished';
                        return file;
                    });
                }, 800);
            })
            .catch((data) => {
                if(data && !data.cancelled) {
                    this.updateFileState(folder, index, (file) => {
                        file.deleting = 'error';
                        return file;
                    });
                }
            });
    };

    renameFile = (folder, from, to, index) => {
        const {execPushing, cancelPromise} = this.props;
        if(from === to) {
            return;
        }
        const newName = folder ? folder + '/' + to : '/' + to;
        cancelPromise(execPushing(
            new Pushing(
                'renameFile',
                'renameFile',
                (resolve, reject) => {
                    this.updateFileState(folder, index, (file) => {
                        file.renaming = 'progress';
                        return file;
                    });

                    (new ApiRequest('file', POST,
                        'files/rename-file/' + (folder ? encodeURIComponent(folder + '/' + from) : encodeURIComponent('/' + from)),
                        {to: newName}))
                        .debug(true)
                        .header({})
                        .exec()
                        .then((res) => {
                            if(res && res.data) {
                                resolve(res.data);
                            } else {
                                reject();
                            }
                        })
                        .catch(() => {
                            reject();
                        });
                }
            )))
            .then((data) => {
                if(data.error) {
                    this.updateFileState(folder, index, (file) => {
                        file.renaming = 'error';
                        return file;
                    });
                    return;
                }

                this.updateFileState(folder, index, (file) => {
                    file.renaming = 'success';
                    file.name = to;
                    return file;
                });

                setTimeout(() => {
                    this.updateFileState(folder, index, (file) => {
                        file.renaming = false;
                        return file;
                    });
                }, 800);
            })
            .catch((data) => {
                if(data && !data.cancelled) {
                    this.updateFileState(folder, index, (file) => {
                        file.renaming = 'error';
                        return file;
                    });
                }
            });
    };

    updateDirState = (folder, index, evt) => {
        let list = {};
        if(folder) {
            const folders = {...this.state.folders};
            list = {...folders[folder]};
        } else {
            list = {...this.state.folderRoot};
        }
        const dirs = [...list.dir];
        let dir = {...dirs[index]};

        dir = evt(dir);

        dir.delete = (folder,) => {
            this.deleteDir(folder, dir.name, index);
        };

        dir.rename = (folder, to) => {
            this.renameDir(folder, dir.name, to, index);
        };

        dirs[index] = dir;
        list.dir = dirs;

        if(folder) {
            this.setState({
                folders: list
            });
        } else {
            this.setState({
                folderRoot: list
            });
        }
    };

    createDir = (folder, newDir) => {
        const {execPushing, cancelPromise} = this.props;

        return cancelPromise(execPushing(
            new Pushing(
                'createDir',
                'createDir',
                (resolve, reject) => {
                    (new ApiRequest('api', POST, 'files/create-dir/' + (folder ? encodeURIComponent(folder + '/' + newDir) : encodeURIComponent('/' + newDir))))
                        .debug(true)
                        .header({})
                        .exec()
                        .then((res) => {
                            if(res && res.data) {
                                resolve(res.data);
                            } else {
                                reject();
                            }
                        })
                        .catch(() => {
                            reject();
                        });
                }
            )));
    };

    deleteDir = (folder, dir, index) => {
        const {execPushing, cancelPromise} = this.props;
        cancelPromise(execPushing(
            new Pushing(
                'deleteDir',
                'deleteDir',
                (resolve, reject) => {
                    this.updateDirState(folder, index, (dir) => {
                        dir.deleting = 'progress';
                        return dir;
                    });

                    (new ApiRequest('file', POST, 'files/delete-dir/' + (folder ? encodeURIComponent(folder + '/' + dir) : encodeURIComponent('/' + dir))))
                        .debug(true)
                        .header({})
                        .exec()
                        .then((res) => {
                            if(res && res.data) {
                                resolve(res.data);
                            } else {
                                reject();
                            }
                        })
                        .catch(() => {
                            reject();
                        });
                }
            )))
            .then((data) => {
                if(data.error) {
                    this.updateDirState(folder, index, (dir) => {
                        dir.deleting = 'error';
                        return dir;
                    });
                    return;
                }

                this.updateDirState(folder, index, (dir) => {
                    dir.deleting = 'success';
                    return dir;
                });

                setTimeout(() => {
                    this.updateDirState(folder, index, (dir) => {
                        dir.deleting = 'finished';
                        return dir;
                    });
                }, 800);
            })
            .catch((data) => {
                if(data && !data.cancelled) {
                    this.updateDirState(folder, index, (dir) => {
                        dir.deleting = 'error';
                        return dir;
                    });
                }
            });
    };

    renameDir = (folder, from, to, index) => {
        const {execPushing, cancelPromise} = this.props;
        if(from === to) {
            return;
        }
        const newName = folder ? folder + '/' + to : '/' + to;
        cancelPromise(execPushing(
            new Pushing(
                'renameDir',
                'renameDir',
                (resolve, reject) => {
                    this.updateDirState(folder, index, (dir) => {
                        dir.renaming = 'progress';
                        return dir;
                    });

                    (new ApiRequest('file', POST,
                        'files/rename-dir/' + (folder ? encodeURIComponent(folder + '/' + from) : encodeURIComponent('/' + from)),
                        {to: newName}))
                        .debug(true)
                        .header({})
                        .exec()
                        .then((res) => {
                            if(res && res.data) {
                                resolve(res.data);
                            } else {
                                reject();
                            }
                        })
                        .catch(() => {
                            reject();
                        });
                }
            )))
            .then((data) => {
                if(data.error) {
                    this.updateDirState(folder, index, (dir) => {
                        dir.renaming = 'error';
                        return dir;
                    });
                    return;
                }

                this.updateDirState(folder, index, (dir) => {
                    dir.renaming = 'success';
                    dir.name = to;
                    return dir;
                });

                setTimeout(() => {
                    this.updateDirState(folder, index, (dir) => {
                        dir.renaming = false;
                        return dir;
                    });
                }, 800);
            })
            .catch((data) => {
                if(data && !data.cancelled) {
                    this.updateDirState(folder, index, (dir) => {
                        dir.renaming = 'error';
                        return dir;
                    });
                }
            });
    };

    render() {
        return (
            <StoreMediaContext.Provider value={this.state}>
                {this.props.children}
            </StoreMediaContext.Provider>
        );
    }
}

const StoreMediaProvider = cancelPromise(withPusher(withApiControl(StoreMediaProviderBase)));

const storeMedia = withContextConsumer(StoreMediaContext.Consumer);

export {StoreMediaProvider, storeMedia};