import { ColumnDecimalTransformer, MathUtils } from '@core/utils'
import { Column, Entity, JoinColumn, JoinTable, ManyToMany, OneToMany, OneToOne } from 'typeorm'
import { SoftDeleteEntity } from '../entity'
import { Reservation } from '../reservation'
import { Table } from '../room-plan'
import { OrderState } from './enums'
import { OrderItem } from './order-item.model'

@Entity()
export class Order extends SoftDeleteEntity<Order> {
	/**
	 * 3 letter Currency code
	 */
	@Column({
		type: 'varchar',
		length: 3,
		nullable: false,
	})
	currencyCode: string

	/**
	 * Total unpaid amount
	 * NOTE: Don't set this property directly, should be update by {@link setItems()} and {@link addItem()}
	 */
	@Column({
		type: 'decimal',
		nullable: false,
		transformer: new ColumnDecimalTransformer(),
	})
	outstandingAmount: number

	/**
	 * Total paid amount
	 * NOTE: Don't set this property directly, should be update by {@link setItems()} and {@link addItem()}
	 */
	@Column({
		type: 'decimal',
		nullable: false,
		transformer: new ColumnDecimalTransformer(),
	})
	paidAmount: number

	/**
	 * Base amount without tax
	 * NOTE: Don't set this property directly, should be update by {@link setItems()} and {@link addItem()}
	 */
	@Column({
		type: 'decimal',
		nullable: false,
		transformer: new ColumnDecimalTransformer(),
	})
	baseAmount: number

	/**
	 * Tax percentage applied on the sub amount
	 */
	@Column({
		type: 'decimal',
		nullable: false,
		transformer: new ColumnDecimalTransformer(),
	})
	taxPercentage: number

	/**
	 * Total tax amount calculated from tax percentage
	 * NOTE: Don't set this property directly, should be update by {@link setItems()} and {@link addItem()}
	 */
	@Column({
		type: 'decimal',
		nullable: false,
		transformer: new ColumnDecimalTransformer(),
	})
	taxAmount: number

	/**
	 * Total amount of the order including tax
	 * NOTE: Don't set this property directly, should be update by {@link setItems()} and {@link addItem()}
	 */
	@Column({
		type: 'decimal',
		nullable: false,
		transformer: new ColumnDecimalTransformer(),
	})
	totalAmount: number

	/**
	 * The current state of the order
	 */
	@Column({
		type: 'enum',
		enum: OrderState,
		default: OrderState.open,
		nullable: false,
	})
	state: OrderState

	/**
	 * The list of items in the order
	 * NOTE: Don't set this directly, instead update items using {@link setItems()} and {@link addItem()} methods
	 */
	@OneToMany(() => OrderItem, (item) => item.order)
	items: OrderItem[]

	/**
	 * The reservation for which order was created
	 */
	@OneToOne(() => Reservation, (reservation) => reservation.order, { nullable: true })
	@JoinColumn()
	reservation?: Reservation

	/**
	 * List of tables for the order
	 */
	@ManyToMany(() => Table)
	@JoinTable({ name: 'order_tables' })
	tables: Table[]

	setItems(items: OrderItem[]) {
		this.items = items
		this.updateAmounts()
	}

	addItem(item: OrderItem) {
		if (!this.items) this.items = []

		this.items.push(item)
		this.updateAmounts()
	}

	private updateAmounts(): void {
		const totalAmount = this.items.map((item) => item.quantity * item.unitPrice).reduce((acc, cur) => acc + cur, 0)

		this.totalAmount = MathUtils.round(totalAmount, 2)

		this.taxAmount = this.taxPercentage === 0 ? 0 : MathUtils.round((this.taxPercentage * this.totalAmount) / 100, 2)

		this.baseAmount = MathUtils.round(this.totalAmount - this.taxAmount, 2)

		const paidAmount = this.items.map((item) => item.paidQuantity * item.unitPrice).reduce((acc, cur) => acc + cur, 0)
		const outstandingAmount = this.totalAmount - paidAmount

		this.paidAmount = MathUtils.round(paidAmount, 2)
		this.outstandingAmount = MathUtils.round(outstandingAmount, 2)
	}
}
