const SyncConnector = require('./sync-connector');
require('./sync-db');
require('./sync-sip');
require('./sync-ldap');
require('./sync-samba');

module.exports = class SyncEngine {
    async init($) {
        let accountConfig = await $.AccountConfig.findOne({
            fields: ['homedir', 'shell', 'idBase']
        });
        let mailConfig = await $.MailConfig.findOne({
            fields: ['domain']
        });

        let connectors = [];

        for (let ConnectorClass of SyncConnector.connectors) {
            let connector = new ConnectorClass();
            Object.assign(connector, {
                engine: this,
                $,
                accountConfig,
                mailConfig
            });
            if (!await connector.init()) continue;
            connectors.push(connector);
        }

        Object.assign(this, {
            connectors,
            $,
            accountConfig,
            mailConfig
        });
    }

    async deinit() {
        for (let connector of this.connectors)
            await connector.deinit();
    }

    async sync(userName, password, syncGroups) {
        let {
            $,
            accountConfig,
            mailConfig
        } = this;

        if (!userName) return;
        userName = userName.toLowerCase();

        // Skip conflicting users
        if (['administrator', 'root'].indexOf(userName) >= 0)
            return;

        let user = await $.Account.findOne({
            where: {name: userName},
            fields: [
                'id',
                'nickname',
                'email',
                'lang',
                'roleFk',
                'sync',
                'active',
                'created',
                'bcryptPassword',
                'updated'
            ],
            include: {
                relation: 'roles',
                scope: {
                    include: {
                        relation: 'inherits',
                        scope: {
                            fields: ['name']
                        }
                    }
                }
            }
        });

        let info = {
            user,
            hasAccount: false
        };

        if (user) {
            let exists = await $.UserAccount.exists(user.id);
            Object.assign(info, {
                hasAccount: user.active && exists,
                corporateMail: `${userName}@${mailConfig.domain}`,
                uidNumber: accountConfig.idBase + user.id
            });
        }

        let errs = [];

        for (let connector of this.connectors) {
            try {
                await connector.sync(info, userName, password);
                if (syncGroups)
                    await connector.syncGroups(info, userName);
            } catch (err) {
                errs.push(err);
            }
        }

        if (errs.length) throw errs[0];
    }

    async syncRoles() {
        for (let connector of this.connectors)
            await connector.syncRoles();
    }

    async getUsers() {
        let usersToSync = new Set();

        for (let connector of this.connectors)
            await connector.getUsers(usersToSync);

        return usersToSync;
    }
};