import * as dotenv from "dotenv";
import chai from "chai";
import * as _ from "lodash";
const chaiHttp = require("chai-http");

chai.use(chaiHttp);

const should = chai.should();

dotenv.config({ path: __dirname + "/.env" });

import * as TestUtils from "../utils/TestUtils";
import * as QueueTestProxy from "../utils/QueueTestProxy";
import { Cloud } from "../../cloud/src/Cloud";
import * as Models from "../../cloud/src/Models";
import { MessageQueue, Queue, BigscreenCloudCommands, MediaServerCommands } from "../../cloud/src/MessageQueue";
import CloudApiServer from "../../cloud/cloud_api/cloud_api";

const cloudApiKey = "f4czwPZdYidGTcWRbhcWAcFiTaDYhYDraDsjYLYO2YXd4LxQqpMpbcdmyxZEBKNZ";
const cloudAdminApiKey = "f4czwPZdYidGTcWRbhcWAcFiTaDYhYDraDsjYLYO2YXd4LxQqpMpbcdmyxZEBKNZ";

describe("Test media server room failover - when a media server dies, any rooms that used that media server need to be reset...", () => {
    const VERSION = "0.9.0";
    const MAX_USERS = 10;
    let testUser = null;
    let testUser2 = null;
    let testRoomSettings : Models.RoomSettings = null;
    let mediaServerAlpha = null;
    let mediaServerBeta = null;
    let mediaServerGamma = null;
    let mediaServerDelta = null;
    let roomId = null;
    let roomId2 = null;

    async function registerMediaServer(mediaServerData) {
        await MessageQueue.sendMessage(Queue.BigscreenCloud, {
            command: BigscreenCloudCommands.RegisterMediaServer,
            payload: mediaServerData
        });

        const bigscreenCloudMessage = await QueueTestProxy.getLastBigscreenCloudMessage();
        bigscreenCloudMessage.should.have.property("command").eql(BigscreenCloudCommands.RegisterMediaServer);
        const mediaServer = await Cloud.registerMediaServer(bigscreenCloudMessage.payload);
        mediaServer.should.have.property("id").eql(mediaServerData.id);
        mediaServer.should.have.property("createdAt");
        mediaServer.should.have.property("version").eql(VERSION);
        mediaServer.should.have.property("ipAddress").eql(mediaServerData.ipAddress);
        mediaServer.should.have.property("load").eql(mediaServerData.load);
    }

    before(async () => {
        await Cloud.initialize();
        await Cloud.flushAll();
        await QueueTestProxy.startTestQueue();
        testUser = await CloudTestUtils.generateRandomTestUser();
        testUser2 = await CloudTestUtils.generateRandomTestUser();
        testRoomSettings = CloudTestUtils.generateCreateRoomRequest(VERSION, 4);
        mediaServerAlpha = CloudTestUtils.generateMediaServer(VERSION, 1);
        mediaServerBeta = CloudTestUtils.generateMediaServer(VERSION, 5);
        mediaServerGamma = CloudTestUtils.generateMediaServer(VERSION, 10);
        mediaServerDelta = CloudTestUtils.generateMediaServer(VERSION, 0);
        
        await Cloud.addBigscreenUser(testUser);
        await Cloud.addBigscreenUser(testUser2);
    });

    it("Media server alpha registration succeeds", async () => {
        registerMediaServer(mediaServerAlpha);
    });

    it("create a room succeeds", async () => {
        const res = await chai.request(CloudApiServer)
            .post("/room")
            .set("Authorization", `Bearer ${apiKey}`)
            .set("x-access-token", testUser.accessToken)
            .send(testRoomSettings.toObject());
        res.should.have.status(200);
        const room = res.body;
        roomId = room.roomId;
    });

    it("Delete the media server.", async () => {
        await Cloud.deleteMediaServer(mediaServerAlpha);
    });

    it("user should be prevented from joining a room because there are no media servers!", async () => {
        const res = await chai.request(CloudApiServer)
            .post("/join_room")
            .set("Authorization", `Bearer ${apiKey}`)
            .set("x-access-token", testUser.accessToken)
            .send({ roomId, version: VERSION });
        res.should.have.status(404);

        // Delete the room.
        await Cloud.adminDeleteRoom(roomId);
    });

    it("Register both media servers again.", async () => {
        await registerMediaServer(mediaServerAlpha);
        await registerMediaServer(mediaServerBeta);
       // await registerMediaServer(mediaServerGamma);
    });

    it("create a room succeeds", async () => {
        const res = await chai.request(CloudApiServer)
            .post("/room")
            .set("Authorization", `Bearer ${apiKey}`)
            .set("x-access-token", testUser.accessToken)
            .send(testRoomSettings.toObject());
        res.should.have.status(200);
        const room = res.body;
        roomId = room.roomId;
    });

    it("join a room succeeds, should have the media server with the lower load.", async () => {
        const res = await chai.request(CloudApiServer)
            .post("/join_room")
            .set("Authorization", `Bearer ${apiKey}`)
            .set("x-access-token", testUser.accessToken)
            .send({ roomId, version: VERSION });
        res.should.have.status(200);

        const room = await Cloud.getLiveRoom(roomId);
        room.should.have.property("mediaServer");
        room.mediaServer.should.have.property("id").eql(mediaServerAlpha.id);
    });

    it("Delete the media server - failover should occur and mediaServerBeta should take over.", async () => {
        await Cloud.deleteMediaServer(mediaServerAlpha);

        const room = await Cloud.getLiveRoom(roomId);
        room.should.have.property("mediaServer");
        room.mediaServer.should.have.property("id").eql(mediaServerBeta.id);
    });

    it("Delete the media server - failover should not occur and the room status should be \"Error\".", async () => {
        await Cloud.deleteMediaServer(mediaServerBeta);

        const room = await Cloud.getLiveRoom(roomId);
        room.should.have.property("mediaServer").eql(undefined);
        room.should.have.property("status").eql(Models.RoomStatus.Error);
    });

    it("User can still leave the room. On leaving, room is deleted.", async () => {
        const res = await chai.request(CloudApiServer)
            .get("/leave_room")
            .set("Authorization", `Bearer ${apiKey}`)
            .set("x-access-token", testUser.accessToken);
        res.should.have.status(200);

        const room = await Cloud.getLiveRoom(roomId);
        chai.expect(room).to.be.equal(null);
    });

    it("Register three media servers.", async () => {
        await registerMediaServer(mediaServerAlpha);
        await registerMediaServer(mediaServerBeta);
        await registerMediaServer(mediaServerGamma);
    });

    it("create a room succeeds", async () => {
        const res = await chai.request(CloudApiServer)
            .post("/room")
            .set("Authorization", `Bearer ${apiKey}`)
            .set("x-access-token", testUser.accessToken)
            .send(testRoomSettings.toObject());
        res.should.have.status(200);
        const room = res.body;
        roomId = room.roomId;
    });

    it("testUser2 creates a room - succeeds", async () => {
        const res = await chai.request(CloudApiServer)
            .post("/room")
            .set("Authorization", `Bearer ${apiKey}`)
            .set("x-access-token", testUser2.accessToken)
            .send(testRoomSettings.toObject());
        res.should.have.status(200);
        const room = res.body;
        roomId2 = room.roomId;

        const room2 = await Cloud.getLiveRoom(roomId2);
        room2.should.have.property("mediaServer");
        room2.mediaServer.should.have.property("id").eql(mediaServerAlpha.id);
    });

    it("Delete the alpha media server - failover should occur and mediaServerBeta should take over on both rooms.", async () => {
        await Cloud.deleteMediaServer(mediaServerAlpha);

        const room = await Cloud.getLiveRoom(roomId);
        room.mediaServer.should.have.property("id").eql(mediaServerBeta.id);

        const room2 = await Cloud.getLiveRoom(roomId2);
        room2.mediaServer.should.have.property("id").eql(mediaServerBeta.id);
    });

    it("Register another media servers - other rooms should still be beta", async () => {
        await registerMediaServer(mediaServerDelta);

        const room = await Cloud.getLiveRoom(roomId);
        room.mediaServer.should.have.property("id").eql(mediaServerBeta.id);

        const room2 = await Cloud.getLiveRoom(roomId2);
        room2.mediaServer.should.have.property("id").eql(mediaServerBeta.id);

        // Delete the rooms.
        await Cloud.adminDeleteRoom(roomId);
        await Cloud.adminDeleteRoom(roomId2);

        await Cloud.deleteMediaServer(mediaServerBeta);
        await Cloud.deleteMediaServer(mediaServerGamma);
        await Cloud.deleteMediaServer(mediaServerDelta);
    });

    after(async () => {
        await QueueTestProxy.stopTestQueue();
    });

    // Scenarios to deal with:
    // ensure two media servers, Create multiple scaleable rooms, add half of test users, add a new media server, add other half of tests, kill one media server, check that all groups updated to other media server.
});
