import {
  IsBoolean,

  IsInt,

  IsOptional
} from "class-validator";
import { Column, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm";
import { RunnerCardIdOutOfRangeError } from '../../errors/RunnerCardErrors';
import { ResponseRunnerCard } from '../responses/ResponseRunnerCard';
import { Runner } from "./Runner";
import { TrackScan } from "./TrackScan";

/**
 * Defines the RunnerCard entity.
 * A runnerCard is a physical representation for a runner.
 * It can be associated with a runner to create scans via the scan station's.
*/
@Entity()
export class RunnerCard {
  /**
   * Autogenerated unique id (primary key).
   */
  @PrimaryGeneratedColumn()
  @IsInt()
  id: number;

  /**
   * The card's currently associated runner.
   * To increase reusability a card can be reassigned.
   */
  @IsOptional()
  @ManyToOne(() => Runner, runner => runner.cards, { nullable: true })
  runner: Runner;

  /**
   * Is the card enabled (for fraud reasons)?
   * Default: true
   */
  @Column()
  @IsBoolean()
  enabled: boolean = true;

  /**
   * The card's associated scans.
   * Used to link cards to track scans.
   */
  @OneToMany(() => TrackScan, scan => scan.track, { nullable: true })
  scans: TrackScan[];

  /**
   * Generates a ean-13 compliant string for barcode generation.
   */
  public get code(): string {
    const multiply = [1, 3];
    let total = 0;
    this.paddedId.split('').forEach((letter, index) => {
      total += parseInt(letter, 10) * multiply[index % 2];
    });
    const checkSum = (Math.ceil(total / 10) * 10) - total;
    return this.paddedId + checkSum.toString();
  }

  /**
   * Returns this card's id as a string padded to the length of 12 characters with leading zeros.
   */
  private get paddedId(): string {
    let id: string = this.id.toString();

    if (id.length > 12) {
      throw new RunnerCardIdOutOfRangeError();
    }
    while (id.length < 12) { id = '0' + id; }

    return id;
  }

  /**
   * Turns this entity into it's response class.
   */
  public toResponse() {
    return new ResponseRunnerCard(this);
  }
}
