import { IsNotEmpty } from 'class-validator';
import { getConnection } from 'typeorm';
import { RunnerNotFoundError } from '../../errors/RunnerErrors';
import { RunnerCard } from '../entities/RunnerCard';
import { ScanStation } from '../entities/ScanStation';
import { TrackScan } from '../entities/TrackScan';
import { CreateScan } from './CreateScan';

/**
 * This classed is used to create a new Scan entity from a json body (post request).
 */
export class CreateTrackScan extends CreateScan {

    /**
     * The scan's associated track.
     * This is used to determine the scan's distance.
     */
    @IsNotEmpty()
    track: number;

    /**
     * The runnerCard associated with the scan.
     * This get's saved for documentation and management purposes.
     */
    @IsNotEmpty()
    card: number;

    /**
     * The scanning station that created the scan.
     * Mainly used for logging and traceing back scans (or errors)
     */
    @IsNotEmpty()
    station: number;

    /**
     * Creates a new Track entity from this.
     */
    public async toScan(): Promise<TrackScan> {
        let newScan: TrackScan = new TrackScan();

        newScan.station = await this.getStation();
        newScan.card = await this.getCard();

        newScan.track = newScan.station.track;
        newScan.runner = newScan.card.runner;

        if (!newScan.runner) {
            throw new RunnerNotFoundError();
        }

        newScan.timestamp = new Date(Date.now()).toString();
        newScan.valid = await this.validateScan(newScan);

        return newScan;
    }

    public async getCard(): Promise<RunnerCard> {
        const track = await getConnection().getRepository(RunnerCard).findOne({ id: this.card }, { relations: ["runner"] });
        if (!track) {
            throw new Error();
        }
        return track;
    }

    public async getStation(): Promise<ScanStation> {
        const track = await getConnection().getRepository(ScanStation).findOne({ id: this.card }, { relations: ["track"] });
        if (!track) {
            throw new Error();
        }
        return track;
    }

    public async validateScan(scan: TrackScan): Promise<boolean> {
        const scans = await getConnection().getRepository(TrackScan).find({ where: { runner: scan.runner }, relations: ["track"] });
        if (scans.length == 0) { return true; }

        const newestScan = scans[0];
        if ((new Date(scan.timestamp).getTime() - new Date(newestScan.timestamp).getTime()) > scan.track.minimumLapTime) {
            return true;
        }

        return false;
    }
}