import {
  IsInt,
  IsNotEmpty,
  IsOptional,
  IsPositive,
  IsString
} from "class-validator";
import { BeforeInsert, BeforeUpdate, Column, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn, TableInheritance } from "typeorm";
import { ResponseRunnerGroup } from '../responses/ResponseRunnerGroup';
import { GroupContact } from "./GroupContact";
import type { Runner } from "./Runner";

/**
 * Defines the RunnerGroup entity.
 * This is used to group runners together (as the name suggests).
*/
@Entity()
@TableInheritance({ column: { name: "type", type: "varchar" } })
export abstract class RunnerGroup {
  /**
   * Autogenerated unique id (primary key).
   */
  @PrimaryGeneratedColumn()
  @IsInt()
  id: number;

  /**
   * The group's name.
   */
  @Column()
  @IsNotEmpty()
  @IsString()
  name: string;

  /**
   * The group's contact.
   * This is mostly a feature for the group managers and public relations.
   */
  @IsOptional()
  @ManyToOne(() => GroupContact, contact => contact.groups, { nullable: true })
  contact?: GroupContact;

  /**
   * The group's associated runners.
   * Used to link runners to a runner group.
   */
  @OneToMany(() => require("./Runner").Runner, (runner: Runner) => runner.group, { nullable: true })
  runners!: Runner[];

  @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);
  }

  /**
   * Returns the total distance ran by this group's runners based on all their valid scans.
  */
  @IsInt()
  public get distance(): number {
    if (!this.runners || this.runners.length == 0) {
      return 0;
    }
    return this.runners.reduce((sum, current) => sum + current.distance, 0);
  }

  /**
   * Returns the total donations a runner has collected based on his linked donations and distance ran.
  */
  @IsInt()
  public get distanceDonationAmount(): number {
    return this.runners.reduce((sum, current) => sum + current.distanceDonationAmount, 0);
  }

  /**
   * Turns this entity into it's response class.
   */
  public abstract toResponse(): ResponseRunnerGroup;
}