/* eslint no-console: "off" */
const path = require('path');
const fs = require('fs-extra');

module.exports = Self => {
    Self.remoteMethodCtx('updateData', {
        description: 'Updates schema data from external provider',
        accessType: 'WRITE',
        returns: {
            type: 'object',
            root: true
        },
        http: {
            path: `/updateData`,
            verb: 'GET'
        }
    });

    Self.updateData = async() => {
        const models = Self.app.models;

        const container = await models.TempContainer.container('edi');
        const tempPath = path.join(container.client.root, container.name);

        const [ftpConfig] = await Self.rawSql('SELECT host, user, password FROM edi.ftpConfig');
        console.debug(`Openning FTP connection to ${ftpConfig.host}...\n`);

        const FtpClient = require('ftps');
        const ftpClient = new FtpClient({
            host: ftpConfig.host,
            username: ftpConfig.user,
            password: ftpConfig.password,
            procotol: 'ftp'
        });

        const files = await Self.rawSql('SELECT fileName, toTable, file, updated FROM edi.fileConfig');

        let remoteFile;
        let tempDir;
        let tempFile;
        for (const file of files) {
            try {
                const fileName = file.file;

                console.debug(`Downloading file ${fileName}...`);

                remoteFile = `codes/${fileName}.ZIP`;
                tempDir = `${tempPath}/${fileName}`;
                tempFile = `${tempPath}/${fileName}.zip`;

                await extractFile({
                    ftpClient: ftpClient,
                    file: file,
                    paths: {
                        remoteFile: remoteFile,
                        tempDir: tempDir,
                        tempFile: tempFile
                    }
                });
            } catch (error) {
                if (fs.existsSync(tempFile))
                    await fs.unlink(tempFile);

                await fs.rmdir(tempDir, {recursive: true});
                console.error(error);
            }
        }

        return true;
    };

    async function extractFile({ftpClient, file, paths}) {
        // Download the zip file
        ftpClient.get(paths.remoteFile, paths.tempFile);

        // Execute download command
        ftpClient.exec(async(err, response) => {
            if (response.error) {
                console.debug(`Error downloading file... ${response.error}`);
                return;
            }

            const AdmZip = require('adm-zip');
            const zip = new AdmZip(paths.tempFile);
            const entries = zip.getEntries();

            zip.extractAllTo(paths.tempDir, false);

            if (fs.existsSync(paths.tempFile))
                await fs.unlink(paths.tempFile);

            await dumpData({file, entries, paths});

            await fs.rmdir(paths.tempDir, {recursive: true});
        });
    }

    async function dumpData({file, entries, paths}) {
        const toTable = file.toTable;
        const baseName = file.fileName;

        for (const zipEntry of entries) {
            const entryName = zipEntry.entryName;
            console.log(`Reading file ${entryName}...`);

            const startIndex = (entryName.length - 10);
            const endIndex = (entryName.length - 4);
            const dateString = entryName.substring(startIndex, endIndex);
            const lastUpdated = new Date();

            // Format string date to a date object
            let updated = null;
            if (file.updated) {
                updated = new Date(file.updated);
                updated.setHours(0, 0, 0, 0);
            }

            lastUpdated.setFullYear(`20${dateString.substring(4, 6)}`);
            lastUpdated.setMonth(parseInt(dateString.substring(2, 4)) - 1);
            lastUpdated.setDate(dateString.substring(0, 2));
            lastUpdated.setHours(0, 0, 0, 0);

            if (updated && lastUpdated <= updated) {
                console.debug(`Table ${toTable} already updated, skipping...`);
                continue;
            }

            console.log('Dumping data...');
            const templatePath = path.join(__dirname, `./sql/${toTable}.sql`);
            const sqlTemplate = fs.readFileSync(templatePath, 'utf8');

            const rawPath = path.join(paths.tempDir, entryName);

            try {
                const tx = await Self.beginTransaction({});
                const options = {transaction: tx};

                await Self.rawSql(`DELETE FROM edi.${toTable}`, null, options);
                await Self.rawSql(sqlTemplate, [rawPath], options);
                await Self.rawSql(`
                        UPDATE edi.fileConfig 
                            SET updated = ? 
                        WHERE fileName = ?
                `, [lastUpdated, baseName], options);

                tx.commit();
            } catch (error) {
                tx.rollback();
                throw error;
            }

            console.log(`Updated table ${toTable}\n`);
        }
    }
};