/*
 * Copyright (C) 2017 Centro de Computacao Cientifica e Software Livre
 * Departamento de Informatica - Universidade Federal do Parana
 *
 * This file is part of blendb.
 *
 * blendb is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * blendb is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with blendb.  If not, see <http://www.gnu.org/licenses/>.
 */

import { expect } from "chai";

import { PostgresAdapter } from "./postgres";
import { Adapter } from "../core/adapter";
import { Fixture } from "../../test/postgres/fixture";

import { ConfigParser } from "../util/configParser";
import { adapterScenario } from "../../test/scenario";

describe("postgres adapter", () => {

    // Initializing
    let config: any;
    let adapter: Adapter;
    let fixture;
    before((done) => {
        config = ConfigParser.parse("config/test.yaml");
        fixture = new Fixture(config.connection);
        fixture.load(config.loadViews, config.struct.create, (err) => {
            if (err) {
                throw err;
            }
            adapter = new PostgresAdapter(config.connection);
            done();
        });
    });
    // Tests
    it("should get data from single materialized view", (done) => {
        let view = adapterScenario.materializedView;
        adapter.getDataFromView(view, (err, result) => {
            expect(err).to.be.a("null");
            expect(result).to.be.an("array");
            expect(result).to.have.length(5);
            let keys: string[] = [];
            keys = keys.concat(view.metrics.map((item) => item.name));
            keys = keys.concat(view.dimensions.map((item) => item.name));
            result.forEach((row) => {
                expect(row).to.be.an("object");
                expect(row).to.have.all.keys(keys);
            });
            done();
        });
    });
    it("should get data from join of 2 views (without selection)", (done) => {
        let view = adapterScenario.noSelectionView;
        adapter.getDataFromView(view, (err, result) => {
            expect(err).to.be.a("null");
            expect(result).to.be.an("array");
            expect(result).to.have.length(1);
            expect(result[0]).to.be.an("object");
            let keys: string[] = [];
            keys = keys.concat(view.metrics.map((item) => item.name));
            keys = keys.concat(view.dimensions.map((item) => item.name));
            result.forEach((row) => {
                expect(row).to.be.an("object");
                expect(row).to.have.all.keys(keys);
            });
            done();
        });
    });
    it("should get data from join of 2 views (with selection)", (done) => {
        let view = adapterScenario.withSelectionView;
        adapter.getDataFromView(view, (err, result) => {
            expect(err).to.be.a("null");
            expect(result).to.be.an("array");
            expect(result).to.have.length(5);
            expect(result[0]).to.be.an("object");
            let keys: string[] = [];
            keys = keys.concat(view.metrics.map((item) => item.name));
            keys = keys.concat(view.dimensions.map((item) => item.name));
            result.forEach((row) => {
                expect(row).to.be.an("object");
                expect(row).to.have.all.keys(keys);
            });
            done();
        });
    });
    it("should get data from single view (with sub-dimension)", (done) => {
        let view = adapterScenario.subDimensionView;
        adapter.getDataFromView(view, (err, result) => {
            expect(err).to.be.a("null");
            expect(result).to.be.an("array");
            expect(result).to.have.length(5);
            expect(result[0]).to.be.an("object");
            let keys: string[] = [];
            keys = keys.concat(view.metrics.map((item) => item.name));
            keys = keys.concat(view.dimensions.map((item) => item.name));
            result.forEach((row) => {
                expect(row).to.be.an("object");
                expect(row).to.have.all.keys(keys);
            });
            done();
        });
    });
    it("should get data from join of 4 views (with selection)", (done) => {
        let view = adapterScenario.join4View;
        adapter.getDataFromView(view, (err, result) => {
            expect(err).to.be.a("null");
            expect(result).to.be.an("array");
            expect(result).to.have.length(5);
            expect(result[0]).to.be.an("object");
            let keys: string[] = [];
            keys = keys.concat(view.metrics.map((item) => item.name));
            keys = keys.concat(view.dimensions.map((item) => item.name));
            result.forEach((row) => {
                expect(row).to.be.an("object");
                expect(row).to.have.all.keys(keys);
            });
            done();
        });
    });

    it("should get data from different sub dimensions with same parent", (done) => {
        let view = adapterScenario.dateView;
        adapter.getDataFromView(view, (err, result) => {
            expect(err).to.be.a("null");
            expect(result).to.be.an("array");
            expect(result).to.have.length(5);
            expect(result[0]).to.be.an("object");
            let keys: string[] = [];
            keys = keys.concat(view.metrics.map((item) => item.name));
            keys = keys.concat(view.dimensions.map((item) => item.name));
            result.forEach((row) => {
                expect(row).to.be.an("object");
                expect(row).to.have.all.keys(keys);
                expect(row).to.have.property("dim:0:year", 2017);
            });
            done();
        });
    });
    it("should get data from view with all types of agreggation", (done) => {
        let view = adapterScenario.aggrView;
        adapter.getDataFromView(view, (err, result) => {
            expect(err).to.be.a("null");
            expect(result).to.be.an("array");
            expect(result).to.have.length(1);
            expect(result[0]).to.be.an("object");
            let keys: string[] = [];
            keys = keys.concat(view.metrics.map((item) => item.name));
            keys = keys.concat(view.dimensions.map((item) => item.name));
            result.forEach((row) => {
                expect(row).to.be.an("object");
                expect(row).to.have.all.keys(keys);
            });

            expect(parseInt(result[0]["met:0"], 10)).to.be.equal(15);
            expect(parseInt(result[0]["met:1"], 10)).to.be.equal(3);
            expect(parseInt(result[0]["met:6"], 10)).to.be.equal(5);
            done();
        });
    });

    it("should get data from view when a single clause exists", (done) => {
        let view = adapterScenario.clauseView;
        adapter.getDataFromView(view, (err, result) => {
            expect(err).to.be.a("null");
            expect(result).to.be.an("array");
            expect(result).to.have.length(1);
            expect(result[0]).to.be.an("object");
            let keys: string[] = [];
            keys = keys.concat(view.metrics.map((item) => item.name));
            keys = keys.concat(view.dimensions.map((item) => item.name));
            result.forEach((row) => {
                expect(row).to.be.an("object");
                expect(row).to.have.all.keys(keys);
            });

            expect(parseInt(result[0]["met:0"], 10)).to.be.equal(1);
            expect(parseInt(result[0]["met:1"], 10)).to.be.equal(1);
            expect(parseInt(result[0]["met:2"], 10)).to.be.equal(1);
            expect(result[0]["dim:0"].getDate()).to.be.equal(1);
            done();
        });
    });

    it("should get data from view with single clause,  with more than on filter", (done) => {
        let view = adapterScenario.multiFilterView;
        adapter.getDataFromView(view, (err, result) => {
            expect(err).to.be.a("null");
            expect(result).to.be.an("array");
            expect(result).to.have.length(2);
            expect(result[0]).to.be.an("object");
            let keys: string[] = [];
            keys = keys.concat(view.metrics.map((item) => item.name));
            keys = keys.concat(view.dimensions.map((item) => item.name));
            result.forEach((row) => {
                expect(row).to.be.an("object");
                expect(row).to.have.all.keys(keys);
            });

            done();
        });
    });

    it("should get data from view with multiple clauses", (done) => {
        let view = adapterScenario.multiClauseView;
        adapter.getDataFromView(view, (err, result) => {
            expect(err).to.be.a("null");
            expect(result).to.be.an("array");
            expect(result).to.have.length(0);

            done();
        });
    });

    it("should get data from view with a clause with not equal operator", (done) => {
        let view = adapterScenario.notEqualView;
        adapter.getDataFromView(view, (err, result) => {
            expect(err).to.be.a("null");
            expect(result).to.be.an("array");
            expect(result).to.have.length(2);
            expect(result[0]).to.be.an("object");
            let keys: string[] = [];
            keys = keys.concat(view.metrics.map((item) => item.name));
            keys = keys.concat(view.dimensions.map((item) => item.name));
            result.forEach((row) => {
                expect(row).to.be.an("object");
                expect(row).to.have.all.keys(keys);
            });

            done();
        });
    });

    it("should get data from view, using > AND < operators on filters", (done) => {
        let view = adapterScenario.gtltView;
        adapter.getDataFromView(view, (err, result) => {
            expect(err).to.be.a("null");
            expect(result).to.be.an("array");
            expect(result).to.have.length(1);
            expect(result[0]).to.be.an("object");
            let keys: string[] = [];
            keys = keys.concat(view.metrics.map((item) => item.name));
            keys = keys.concat(view.dimensions.map((item) => item.name));
            result.forEach((row) => {
                expect(row).to.be.an("object");
                expect(row).to.have.all.keys(keys);
            });

            done();
        });
    });

    it("should get data from view, using >= AND <= operators on filters", (done) => {
        let view = adapterScenario.geleView;
        adapter.getDataFromView(view, (err, result) => {
            expect(err).to.be.a("null");
            expect(result).to.be.an("array");
            expect(result).to.have.length(3);
            expect(result[0]).to.be.an("object");
            let keys: string[] = [];
            keys = keys.concat(view.metrics.map((item) => item.name));
            keys = keys.concat(view.dimensions.map((item) => item.name));
            result.forEach((row) => {
                expect(row).to.be.an("object");
                expect(row).to.have.all.keys(keys);
            });

            done();
        });
    });
});