import {
  IsBoolean,

  IsInt,

  IsOptional,
  IsPositive
} from "class-validator";
import { BeforeInsert, BeforeUpdate, Column, Entity, Index, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm";
import { RunnerCardIdOutOfRangeError } from '../../errors/RunnerCardErrors';
import { ResponseRunnerCard } from '../responses/ResponseRunnerCard';
import type { Runner } from "./Runner";
import type { 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()
@Index(['runner'])
@Index(['enabled'])
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(() => require("./Runner").Runner, (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(() => require("./TrackScan").TrackScan, (scan: TrackScan) => scan.card, { nullable: true })
  scans!: TrackScan[];

  @Column({ type: 'bigint', nullable: true, readonly: true })
  @IsInt()
  @IsPositive()
  created_at: number;

  @Column({ type: 'bigint', nullable: true })
  @IsInt()
  @IsPositive()
  updated_at: number;

  @BeforeInsert()
  public setCreatedAt() {
    this.created_at = Math.floor(Date.now() / 1000);
    this.updated_at = Math.floor(Date.now() / 1000);
  }

  @BeforeUpdate()
  public setUpdatedAt() {
    this.updated_at = Math.floor(Date.now() / 1000);
  }

  /**
   * Generates a ean-13 compliant string for barcode generation.
   */
  public get code(): string {
    return this.paddedId
  }

  /**
   * 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 > 11) {
      throw new RunnerCardIdOutOfRangeError();
    }
    while (id.length < 11) { id = '0' + id; }
    id = '2' + id;

    return id;
  }

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