import { Entity, PrimaryGeneratedColumn, Column, BaseEntity, OneToMany, OneToOne, JoinColumn, CreateDateColumn, MoreThanOrEqual } from "typeorm";
import bcrypt from "bcrypt";
import { MovieTicket } from "./MovieTicket";
import { MovieQuota } from "./MovieQuota";
import { DiscordAccount } from "./DiscordAccount";

@Entity()
export class User extends BaseEntity
{
	@PrimaryGeneratedColumn()
	id!: number;

	@Column()
	isAdmin!: boolean;

	@Column({ length: 50 })
	name!: string;

	@Column({ length: 255 })
	email!: string;

	@Column({ type: "char", length: 60 })
	password!: string;

	@CreateDateColumn()
	createdAt!: Date;

	@OneToOne(() => MovieQuota, { nullable: true })
	@JoinColumn()
	quota!: MovieQuota | null;

	@OneToMany(() => User, user => user.movieTickets)
	movieTickets!: MovieTicket[];

	@OneToMany(() => DiscordAccount, account => account.user)
	discordAccounts!: DiscordAccount[];

	/**
	 * Authenticate a user and return an auth token upon success
	 */
	public static async authenticate(email: string, password: string) {
		let user = <User>await User.findOne({ email });
		if (user === undefined || !(await bcrypt.compare(password, user.password))) {
			return null;
		}
		return user;
	}

	/**
	 * Create a new user
	 */
	public static async createUser(name: string, email: string, password: string, quota: number|null = 5) {
		let user = new User();
		user.isAdmin = false;
		user.name = name;
		user.email = email;
		user.password = await bcrypt.hash(password, 8);
		// Create a quota if necessary
		if (quota !== null) {
			user.quota = new MovieQuota;
			user.quota.moviesPerWeek = quota;
			await user.quota.save();
		}
		return await user.save();
	}

	/**
	 * Determine the user's available quota
	 */
	public async availableQuota() {
		let quota = await this.fetchQuota();
		if (quota === null) {
			return null;
		}
		let oneWeekAgo = new Date(Date.now() - 1000*60*60*24*7);
		let numTicketsThisWeek = await MovieTicket.count({
			user: this,
			createdAt: MoreThanOrEqual(oneWeekAgo),
			isCanceled: false
		});
		return quota.moviesPerWeek - numTicketsThisWeek;
	}

	/**
	 * Get the user's quota, fetching it if undefined
	 */
	public async fetchQuota() {
		if (this.quota !== undefined) {
			return this.quota;
		}
		let user = <User>await User.findOne(this.id, { relations: ["quota"] });
		return user.quota;
	}

	/**
	 * Fetch active movie tickets for this user
	 */
	public async activeMovieTickets() {
		return await MovieTicket.find({
			where: { user: this, isCanceled: false, isFulfilled: false },
			relations: ["info"]
		});
	}
}
