import { IsNotEmpty, IsOptional, IsString } from 'class-validator';
import { getConnectionManager } from 'typeorm';
import { RunnerGroupNeededError } from '../../errors/RunnerErrors';
import { RunnerOrganizationNotFoundError } from '../../errors/RunnerOrganizationErrors';
import { RunnerGroup } from '../entities/RunnerGroup';
import { RunnerOrganization } from '../entities/RunnerOrganization';
import { RunnerTeam } from '../entities/RunnerTeam';
import { CreateRunner } from './create/CreateRunner';

/**
 * Special class used to import runners from csv files - or json arrays created from csv to be exact.
 * Why you ask? Because the past has shown us that a non excel/csv based workflow is too much for most schools.
 */
export class ImportRunner {

    /**
     * The new runner's first name.
     */
    @IsString()
    @IsNotEmpty()
    firstname: string;

    /**
     * The new runner's middle name.
     */
    @IsString()
    @IsOptional()
    middlename?: string;

    /**
     * The new runner's last name.
     */
    @IsString()
    @IsNotEmpty()
    lastname: string;

    /**
     * The new runner's team's name (if not provided otherwise).
     * The team will automaticly get generated if it doesn't exist in this org yet.
     */
    @IsString()
    @IsOptional()
    team?: string;

    /**
     * Just an alias for team, because this is usually only used for importing data from schools.
     */
    @IsOptional()
    @IsString()
    public set class(value: string) {
        this.team = value;
    }

    /**
     * Creates a CreateRunner object based on this.
     * @param groupID Either the id of the new runner's group or the id of the org that the new runner's team is a part of.
     */
    public async toCreateRunner(groupID: number): Promise<CreateRunner> {
        let newRunner: CreateRunner = new CreateRunner();

        newRunner.firstname = this.firstname;
        newRunner.middlename = this.middlename;
        newRunner.lastname = this.lastname;
        newRunner.group = (await this.getGroup(groupID)).id;

        return newRunner;
    }

    /**
     * Get's the new runners group.
     * @param groupID Either the id of the new runner's group or the id of the org that the new runner's team is a part of.
     */
    public async getGroup(groupID: number): Promise<RunnerGroup> {
        if (this.team === undefined && groupID === undefined) {
            throw new RunnerGroupNeededError();
        }

        let team = await getConnectionManager().get().getRepository(RunnerTeam).findOne({ id: groupID });
        if (team) { return team; }

        let org = await getConnectionManager().get().getRepository(RunnerOrganization).findOne({ id: groupID });
        if (!org) {
            throw new RunnerOrganizationNotFoundError();
        }
        if (this.team === undefined) { return org; }

        team = await getConnectionManager().get().getRepository(RunnerTeam).findOne({ name: this.team, parentGroup: org });
        if (!team) {
            let newRunnerTeam: RunnerTeam = new RunnerTeam();
            newRunnerTeam.name = this.team;
            newRunnerTeam.parentGroup = org;
            team = await getConnectionManager().get().getRepository(RunnerTeam).save(newRunnerTeam);
        }

        return team;
    }
}