// tslint:disable: no-unused-expression
import { expect } from "chai";
import sinon from "sinon";
import { LogLevel } from "../lib/log-level";
import { Logger } from "../lib/logger";
import { ITransport } from "../lib/transport";

describe("Logger", () => {
    let transport: ITransport;
    let sut: Logger;

    beforeEach(() => {
        transport = { write: sinon.stub() };
        sut = new Logger();
        sut.transports.push(transport);
    });

    it("should default log level of Info", () => {
        expect(sut.level).to.equal(LogLevel.Info);
    });

    it("should ignore messages below setting", () => {
        const logger = sut.sub("area");
        expect((transport.write as sinon.SinonSpy).callCount).to.equal(0);
        logger.debug("testing");
        expect((transport.write as sinon.SinonSpy).callCount).to.equal(0);
    });

    describe("create logger with instance", () => {
        it("should create logger with instance", () => {
            const logger = sut.sub("area", "instance");
            expect(logger.area).to.equal("area");
            expect(logger.instance).to.equal("instance");
        });

        it("should write a message to the transport", () => {
            const logger = sut.sub("area", "instance");
            logger.info("testing");
            expect((transport.write as sinon.SinonSpy).args[0][0]).to.match(
                /\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d\.\d\d\dZ \[INF\] area instance: testing/,
            );
        });
    });

    describe("nested sub logger", () => {
        it("should point to root", () => {
            sut.level = LogLevel.Debug;
            const level2 = sut.sub("level2");
            const level3 = sut.sub("level3");
            level3.debug("test");
            expect((transport.write as sinon.SinonSpy).args[0][0]).contain("test");
        });
    });

    describe("create logger without instance", () => {
        it("should create logger without an instance", () => {
            const logger = sut.sub("area");
            expect(logger.area).to.equal("area");
            expect(logger.instance).to.be.undefined;
        });

        it("should write a message to the transport", () => {
            const logger = sut.sub("area");
            logger.info("testing");
            expect((transport.write as sinon.SinonSpy).args[0][0]).to.match(
                /\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d\.\d\d\dZ \[INF\] area: testing/,
            );
        });
    });

    const fixtures = [
        { fn: "trace", level: LogLevel.Trace },
        { fn: "debug", level: LogLevel.Debug },
        { fn: "info", level: LogLevel.Info },
        { fn: "warn", level: LogLevel.Warn },
        { fn: "error", level: LogLevel.Error },
    ];

    for (const { fn, level } of fixtures) {
        describe("." + fn, () => {
            beforeEach(() => {
                sut = new Logger("area", "instance");
                sut.level = LogLevel.Trace;
                sut.transports.push(transport);
            });

            it("should have message with level " + level, () => {
                sut[fn]("testing");
                expect((transport.write as sinon.SinonSpy).args[0][0]).to.match(
                    new RegExp("[" + level + "]"),
                );
            });

            it("should have message with area", () => {
                sut[fn]("testing");
                expect((transport.write as sinon.SinonSpy).args[0][0]).to.match(/ area/);
            });

            it("should have message with instance", () => {
                sut[fn]("testing");
                expect((transport.write as sinon.SinonSpy).args[0][0]).to.match(/ instance/);
            });

            it("should have message with actual message", () => {
                sut[fn]("testing");
                expect((transport.write as sinon.SinonSpy).args[0][0]).to.match(/ testing$/);
            });

            it("should call write with single variable message", () => {
                sut[fn](5);
                expect((transport.write as sinon.SinonSpy).args[0][0]).to.match(/5$/);
            });

            it("should call write with sprintf message", () => {
                sut[fn]("testing %s, %d, %j", "hello", 5, { foo: "bar" });
                expect((transport.write as sinon.SinonSpy).args[0][0]).to.contain(
                    'testing hello, 5, {"foo":"bar"}',
                );
            });

            it("should call write with variadic message", () => {
                sut[fn]("testing", 1, 2, 3, 4, "5");
                expect((transport.write as sinon.SinonSpy).args[0][0]).to.contain(
                    "testing 1 2 3 4 5",
                );
            });

            it("should work unbounded", () => {
                const debug = sut[fn];
                debug("testing");
                expect((transport.write as sinon.SinonSpy).called).to.be.true;
            });
        });
    }
});
