import axios from "axios"
import faker from "faker"
import { Runner } from '../models/Runner'
import { RunnerCard } from '../models/RunnerCard'
import { RunnerGroup } from '../models/RunnerGroup'

const baseurl = "http://localhost:4010"

axios.interceptors.request.use((config) => {
    config.headers['request-startTime'] = process.hrtime()
    return config
})

axios.interceptors.response.use((response) => {
    const start = response.config.headers['request-startTime']
    const end = process.hrtime(start)
    const milliseconds = Math.round((end[0] * 1000) + (end[1] / 1000000))
    response.headers['request-duration'] = milliseconds
    return response
})

function generateRunners(amount: number): Runner[] {
    let runners: Runner[] = new Array<Runner>();
    let group = new RunnerGroup();
    let runner = new Runner();
    for (var i = 0; i < amount; i++) {
        group.name = faker.company.bsBuzz();
        group.id = Math.floor(Math.random() * (9999999 - 1) + 1);
        runner.firstname = faker.name.firstName();
        runner.lastname = faker.name.lastName();
        runner.id = Math.floor(Math.random() * (9999999 - 1) + 1);
        runners.push(runner);
    }
    return runners;
}

function generateCards(amount: number): RunnerCard[] {
    let cards: RunnerCard[] = new Array<RunnerCard>();
    let card = new RunnerCard();
    for (let runner of generateRunners(amount)) {
        card.id = runner.id;
        card.code = idToEan13(card.id);
        card.runner = runner;
        cards.push(card);
    }
    return cards;
}

function idToEan13(id): string {
    const multiply = [1, 3];
    id = id.toString();

    if (id.length > 12) {
        throw new Error("id too long");
    }
    while (id.length < 12) { id = '0' + id; }

    let total = 0;
    id.split('').forEach((letter, index) => {
        total += parseInt(letter, 10) * multiply[index % 2];
    });
    const checkSum = (Math.ceil(total / 10) * 10) - total;
    return id + checkSum.toString();
}

async function postContracts(runners: Runner[]): Promise<Measurement> {
    const res = await axios.post(`${baseurl}/contracts`, runners);
    return new Measurement("contract", runners.length, parseInt(res.headers['request-duration']))
}

async function postCards(cards: RunnerCard[]): Promise<Measurement> {
    const res = await axios.post(`${baseurl}/cards`, cards);
    return new Measurement("card", cards.length, parseInt(res.headers['request-duration']))
}

async function testContracts(sizes): Promise<Measurement[]> {
    let measurements = new Array<Measurement>();
    console.log("#### Testing contracts ####");

    for (let size of sizes) {
        const m = await postContracts(generateRunners(size));
        console.log(m.toString());
        measurements.push(m);
    }
    return measurements;
}

async function testCards(sizes): Promise<Measurement[]> {
    let measurements = new Array<Measurement>();
    console.log("#### Testing Cards ####");

    for (let size of sizes) {
        const m = await postCards(generateCards(size));
        console.log(m.toString());
        measurements.push(m);
    }
    return measurements;
}

async function main() {
    const sizes = [0, 1, 10, 50, 100, 200, 500, 1000]
    console.log("########### Speedtest ###########");
    console.log(`Document server version (according to the api): ${(await axios.get("http://localhost:4010/version")).data.version}`);
    console.log("####### Running tests #######");
    const contractResults = await testContracts(sizes);
    const cardResults = await testCards(sizes);
    console.log("####### Results #######");
    console.table(contractResults);
    console.table(cardResults);
}

main();

class Measurement {
    public type: string;
    public inputcount: number;
    public responsetime: number;

    constructor(type: string, input: number, time: number) {
        this.type = type;
        this.inputcount = input;
        this.responsetime = time;
    }

    public toString(): string {
        return `It took ${this.responsetime}ms to generate ${this.inputcount} pdfs for the type ${this.type}.`
    }
}