import csv from 'csvtojson';
import { Authorized, Body, ContentType, Controller, Param, Post, QueryParam, Req, UseBefore } from 'routing-controllers';
import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
import { RunnerGroupNeededError } from '../errors/RunnerErrors';
import { RunnerGroupNotFoundError } from '../errors/RunnerGroupErrors';
import RawBodyMiddleware from '../middlewares/RawBody';
import { ImportRunner } from '../models/actions/ImportRunner';
import { ResponseRunner } from '../models/responses/ResponseRunner';
import { RunnerController } from './RunnerController';

@Controller()
@Authorized(["RUNNER:IMPORT", "TEAM:IMPORT"])
@OpenAPI({ security: [{ "AuthToken": [] }, { "RefreshTokenCookie": [] }] })
export class ImportController {
    private runnerController: RunnerController;

    /**
     * Gets the repository of this controller's model/entity.
     */
    constructor() {
        this.runnerController = new RunnerController();
    }

    @Post('/runners/import')
    @ContentType("application/json")
    @ResponseSchema(ResponseRunner, { isArray: true, statusCode: 200 })
    @ResponseSchema(RunnerGroupNotFoundError, { statusCode: 404 })
    @ResponseSchema(RunnerGroupNeededError, { statusCode: 406 })
    @OpenAPI({ description: "Create new runners from json and insert them into the provided group. <br> If teams/classes are provided alongside the runner's name they'll automaticly be created under the provided org and the runners will be inserted into the teams instead." })
    async postJSON(@Body({ validate: true, type: ImportRunner }) importRunners: ImportRunner[], @QueryParam("group") groupID: number) {
        if (!groupID) { throw new RunnerGroupNeededError(); }
        let responseRunners: ResponseRunner[] = new Array<ResponseRunner>();
        for await (let runner of importRunners) {
            responseRunners.push(await this.runnerController.post(await runner.toCreateRunner(groupID)));
        }
        return responseRunners;
    }

    @Post('/organizations/:id/import')
    @ContentType("application/json")
    @ResponseSchema(ResponseRunner, { isArray: true, statusCode: 200 })
    @ResponseSchema(RunnerGroupNotFoundError, { statusCode: 404 })
    @ResponseSchema(RunnerGroupNeededError, { statusCode: 406 })
    @OpenAPI({ description: "Create new runners from json and insert them into the provided org. <br> If teams/classes are provided alongside the runner's name they'll automaticly be created under the provided org and the runners will be inserted into the teams instead." })
    async postOrgsJSON(@Body({ validate: true, type: ImportRunner }) importRunners: ImportRunner[], @Param('id') id: number) {
        return await this.postJSON(importRunners, id)
    }

    @Post('/teams/:id/import')
    @ContentType("application/json")
    @ResponseSchema(ResponseRunner, { isArray: true, statusCode: 200 })
    @ResponseSchema(RunnerGroupNotFoundError, { statusCode: 404 })
    @ResponseSchema(RunnerGroupNeededError, { statusCode: 406 })
    @OpenAPI({ description: "Create new runners from json and insert them into the provided team" })
    async postTeamsJSON(@Body({ validate: true, type: ImportRunner }) importRunners: ImportRunner[], @Param('id') id: number) {
        return await this.postJSON(importRunners, id)
    }

    @Post('/runners/import/csv')
    @ContentType("application/json")
    @UseBefore(RawBodyMiddleware)
    @ResponseSchema(ResponseRunner, { isArray: true, statusCode: 200 })
    @ResponseSchema(RunnerGroupNotFoundError, { statusCode: 404 })
    @ResponseSchema(RunnerGroupNeededError, { statusCode: 406 })
    @OpenAPI({ description: "Create new runners from csv and insert them into the provided group. <br> If teams/classes are provided alongside the runner's name they'll automaticly be created under the provided org and the runners will be inserted into the teams instead." })
    async postCSV(@Req() request: any, @QueryParam("group") groupID: number) {
        let csvParse = await csv({ delimiter: [",", ";"], trim: true }).fromString(request.rawBody.toString());
        let importRunners: ImportRunner[] = new Array<ImportRunner>();
        for await (let runner of csvParse) {
            let newImportRunner = new ImportRunner();
            newImportRunner.firstname = runner.firstname;
            newImportRunner.middlename = runner.middlename;
            newImportRunner.lastname = runner.lastname;
            if (runner.class === undefined) { newImportRunner.team = runner.team; }
            else { newImportRunner.class = runner.class; }
            importRunners.push(newImportRunner);
        }
        return await this.postJSON(importRunners, groupID);
    }

    @Post('/organizations/:id/import/csv')
    @ContentType("application/json")
    @UseBefore(RawBodyMiddleware)
    @ResponseSchema(ResponseRunner, { isArray: true, statusCode: 200 })
    @ResponseSchema(RunnerGroupNotFoundError, { statusCode: 404 })
    @ResponseSchema(RunnerGroupNeededError, { statusCode: 406 })
    @OpenAPI({ description: "Create new runners from csv and insert them into the provided org. <br> If teams/classes are provided alongside the runner's name they'll automaticly be created under the provided org and the runners will be inserted into the teams instead." })
    async postOrgsCSV(@Req() request: any, @Param("id") id: number) {
        return await this.postCSV(request, id);
    }

    @Post('/teams/:id/import/csv')
    @ContentType("application/json")
    @UseBefore(RawBodyMiddleware)
    @ResponseSchema(ResponseRunner, { isArray: true, statusCode: 200 })
    @ResponseSchema(RunnerGroupNotFoundError, { statusCode: 404 })
    @ResponseSchema(RunnerGroupNeededError, { statusCode: 406 })
    @OpenAPI({ description: "Create new runners from csv and insert them into the provided team" })
    async postTeamsCSV(@Req() request: any, @Param("id") id: number) {
        return await this.postCSV(request, id);
    }
}