const fs = require('fs-extra');
const debug = require('debug')('server-connect:upload');
const { join, basename, extname } = require('path');
const { toAppPath, toSystemPath, toSiteUrl, parseTemplate, getUniqFile } = require('../core/path');
const diacritics = require('../core/diacritics');

module.exports = {

    upload: async function(options) {
        let self = this;
        let fields = this.parse(options.fields || this.parse('{{$_POST}}'));
        let path = this.parseOptional(options.path, 'string', '/uploads');
        let overwrite = this.parseOptional(options.overwrite, 'boolean', false);
        let createPath = this.parseOptional(options.createPath, 'boolean', true);
        let throwErrors = this.parseOptional(options.throwErrors, 'boolean', false);
        let template = typeof options.template == 'string' ? options.template : false; //this.parseOptional(options.template, 'string', '');
        let replaceSpace = this.parseOptional(options.replaceSpace, 'boolean', false);
        let asciiOnly = this.parseOptional(options.asciiOnly, 'boolean', false);
        let replaceDiacritics = this.parseOptional(options.replaceDiacritics, 'boolean', false);

        if (throwErrors) {
            for (let field in this.req.files) {
                if (Array.isArray(this.req.files[field])) {
                    for (let file of this.req.files[field]) {
                        if (file.truncated) {
                            throw new Error('Some files failed to upload.');
                        }
                    }
                } else {
                    let file = this.req.files[field];
                    if (file.truncated) {
                        throw new Error('Some files failed to upload.');
                    }
                }
            }
        }

        path = toSystemPath(path);

        if (!fs.existsSync(path)) {
            if (createPath) {
                await fs.ensureDir(path);
            } else {
                throw new Error(`Upload path doesn't exist.`);
            }
        }

        let files = this.req.files;
        let uploaded = [];
        
        if (files) {
            await processFields(fields);
        }

        return typeof fields == 'string' ? (uploaded.length ? uploaded[0] : null) : uploaded;

        async function processFields(fields) {
            debug('Process fields: %O', fields);
        
            if (typeof fields == 'object') {
                for (let i in fields) {
                    await processFields(fields[i]);
                }
            } else if (typeof fields == 'string' && files[fields]) {
                let processing = files[fields];

                if (!Array.isArray(processing)) processing = [processing];

                for (let file of processing) {
                    debug('Processing file: %O', file);
                    
                    if (!file.processed) {
                        let name = file.name.replace(/[\x00-\x1f\x7f!%&#@$*()?:,;"'<>^`|+={}\[\]\\\/]/g, '');
                
                        if (replaceSpace) name = name.replace(/\s+/g, '_');
                        if (replaceDiacritics) name = diacritics.replace(name);
                        if (asciiOnly) name = name.replace(/[^\x00-\x7e]/g, '');

                        let filepath = join(path, name);

                        if (template) {
                            let _template = self.parse(template, self.scope.create({
                                file: basename(filepath),
                                name: basename(filepath, extname(filepath)),
                                ext: extname(filepath)
                            }));
                            filepath = parseTemplate(filepath, _template);
                        }

                        if (fs.existsSync(filepath)) {
                            if (overwrite) {
                                await fs.unlink(filepath);
                            } else {
                                filepath = getUniqFile(filepath);
                            }
                        }

                        await file.mv(filepath);

                        uploaded.push({
                            name: basename(filepath),
                            path: toAppPath(filepath),
                            url: toSiteUrl(filepath),
                            type: file.mimetype,
                            size: file.size
                        });
                        
                        file.processed = true;
                    }
                }
            }
        
            return uploaded;
        }
        
    }

};