'use strict';

const path = require('path');
const Dblize = require('paradox-dblize');
const StorageManager = require('paradox-google-storage');
const PubsubManager = require('paradox-google-pubsub');
const _ = require('lodash');
const Sequelize = require('sequelize');
const fs = require('fs');

class CloudManager {

    constructor(adapter, options) {
        this.options = _.defaults(options || {}, {
            adapter: adapter,
            namespace: null,
            credentials: null,
            sync: false,
            models: './models/' + adapter,
        });

        this.models = {};
        this.paths = [];
        this.linkObj = this.createSync();
        this.linkPromise = Promise.resolve(this.linkObj);
    }

    /* -------------------------------------------------------------------------
     * Path
     * ---------------------------------------------------------------------- */

    registerPath(pathName) {
        this.paths.push(pathName);
        return this;
    }

    /* -------------------------------------------------------------------------
     * Link
     * ---------------------------------------------------------------------- */

    /**
     * @alias getLink
     */
    link(options) {
        return this.getLink(options);
    }

    getLink(options) {
        options = _.defaults(options, {
            sync: false,
        });

        if(options.sync) {
            return this.linkObj;
        }

        return this.linkPromise;
    }

    createSync() {
        let link = null;

        if(this.options.adapter == 'sql') {
            let connOpts = this.options.credentials;

            let db = connOpts.credentials.database;
            let username = connOpts.credentials.username;
            let password = connOpts.credentials.password;
            let opts = connOpts.credentials.options;

            link = new Sequelize(db, username, password, opts);

        } else if(this.options.adapter == 'storage') {
            link = new StorageManager(this.options);

        } else if(this.options.adapter == 'pubsub') {
            link = new PubsubManager(this.options);

        } else {
            link = new Dblize(this.options);
        }

        return link;
    }

    create() {
        return Promise.resolve(this.createSync());
    }

    /* -------------------------------------------------------------------------
     * Topics
     * ---------------------------------------------------------------------- */

    topic(key, link) {
        let linkPromise = link ? Promise.resolve(link) : this.getLink();

        return linkPromise
            .then(link => {
                return link.getTopic(key);
            });
    }

    /* -------------------------------------------------------------------------
     * Storage
     * ---------------------------------------------------------------------- */

    /**
     * @alias getBucket
     */
    bucket(key, link) {
        return this.getBucket(key, link);
    }

    /**
     * For google storage
     *
     * @param key
     * @param link
     * @returns {*}
     */
    getBucket(key, link) {
        let linkPromise = link ? Promise.resolve(link) : this.getLink();

        return linkPromise
            .then(link => {
                return link.getBucket(key);
            });
    }

    /* -------------------------------------------------------------------------
     * Models
     * ---------------------------------------------------------------------- */

    /**
     * @alias getModel
     */
    model(key, link, options) {
        return this.getModel(key, link, options);
    }

    /**
     * For bigquery, datastore, elasticsearch, sql
     *
     * @param key
     * @param link
     * @param options
     */
    getModel(key, link, options) {
        options = _.defaults(options, {
            sync: false,
        });

        let modelKey = key;

        let modelFile = path.resolve(this.options.models, key + '.js');

        if(!fs.existsSync(modelFile)) {
            let found = false;

            _.forEach(this.paths, (subPath) => {
                modelFile = path.resolve(subPath, key + '.js');

                if(fs.existsSync(modelFile)) {
                    found = true;
                    return false;
                }
            });

            if(!found) {
                throw new Error('model file not found: ' + key);
            }
        }

        let modelFnc = require(modelFile);

        if(options.sync) {
            let linkObj = link ? link : this.link({sync: true});

            if(!(modelKey in this.models)) {
                this.models[modelKey] = modelFnc(linkObj);
            }

            return this.models[key];
        }

        let linkPromise = link ? Promise.resolve(link) : this.link();

        return linkPromise
            .then(link => {
                if(!(modelKey in this.models)) {
                    this.models[modelKey] = modelFnc(link);
                }

                if(this.options.sync) {
                    return this.models[modelKey].sync();
                }

                return this.models[key];
            });
    }

}

module.exports = CloudManager;