import {
  Entity,
  Index,
  ManyToOne,
  OneToMany,
  Property,
  Collection,
  Cascade,
  Enum,
  BeforeCreate,
  Unique,
  type EventArgs,
} from '@mikro-orm/core';
import { BaseEntity } from '../../../common/entities/base.entity';
import { OrderCouponDetail, OrderStatus } from '../interface/order.interface';
import { User } from '../../users/entities/user.entity';
import { Restaurant } from '../../restaurants/entities/restaurant.entity';
import { PaymentMethod } from '../../payments/entities/payment-method.entity';
import { OrderItem } from './order-item.entity';
import { Delivery } from '../../delivery/entities/delivery.entity';
import { OrderUserAddress, OrderCarAddress } from '../interface/order.interface';
import { Payment } from 'src/modules/payments/entities/payment.entity';

@Entity({ tableName: 'orders' })
@Index({ properties: ['restaurant'] })
@Index({ properties: ['restaurant', 'status'] })
@Index({ properties: ['restaurant', 'orderNumber'] })
export class Order extends BaseEntity {
  @ManyToOne(() => User)
  user!: User;

  @ManyToOne(() => Restaurant)
  restaurant!: Restaurant;

  @ManyToOne(() => Delivery)
  deliveryMethod!: Delivery;

  @OneToMany(() => Payment, payment => payment.order, {
    cascade: [Cascade.ALL],
    orphanRemoval: true,
  })
  payments = new Collection<Payment>(this);

  @OneToMany(() => OrderItem, item => item.order, {
    cascade: [Cascade.ALL],
    orphanRemoval: true,
  })
  items = new Collection<OrderItem>(this);

  @Property({ type: 'json', nullable: true })
  userAddress?: OrderUserAddress | null;
  // for car delivery
  @Property({ type: 'json', nullable: true })
  carAddress?: OrderCarAddress | null;

  @ManyToOne(() => PaymentMethod)
  paymentMethod: PaymentMethod;

  @Property({ type: 'int', nullable: true })
  orderNumber?: number;

  @Property({ type: 'decimal', precision: 10, scale: 0, default: 0 })
  couponDiscount: number = 0;

  @Property({ type: 'jsonb', nullable: true })
  couponDetail?: OrderCouponDetail | null;

  @Property({ type: 'decimal', precision: 10, scale: 0, default: 0 })
  itemsDiscount: number = 0;

  @Property({ type: 'decimal', precision: 10, scale: 0, default: 0 })
  totalDiscount: number = 0;

  @Property({ type: 'decimal', precision: 10, scale: 0 })
  subTotal!: number;

  @Property({ type: 'decimal', precision: 10, scale: 0, default: 0 })
  tax: number = 0;

  @Property({ type: 'decimal', precision: 10, scale: 0, default: 0 })
  deliveryFee: number = 0;

  @Property({ type: 'decimal', precision: 10, scale: 0 })
  total!: number;

  @Property({ type: 'int', default: 0 })
  totalItems: number = 0;

  @Property({ type: 'text', nullable: true })
  description?: string;

  @Property({ nullable: true })
  tableNumber?: string;

  @Enum(() => OrderStatus)
  status!: OrderStatus;

  @Property({ type: 'json', nullable: true })
  history: Array<{ status: OrderStatus; changedAt: Date; desc: string | null }> = [];

  @BeforeCreate()
  async generateOrderNumber(args: EventArgs<Order>) {
    const em = args.em;
    const order = args.entity;

    // Ensure restaurant is loaded
    if (!order.restaurant) {
      throw new Error('Restaurant must be set before creating order');
    }

    // Get the restaurant ID (handle both entity and ID cases)
    const restaurantId = typeof order.restaurant === 'string' ? order.restaurant : order.restaurant.id;

    // Query for max orderNumber for this restaurant
    // Using findOne with orderBy to get the highest order number
    const maxOrder = await em.findOne(
      Order,
      { restaurant: restaurantId },
      {
        orderBy: { orderNumber: 'DESC' },
        fields: ['orderNumber'],
      },
    );

    // Set the next order number (1 if no orders exist, otherwise max + 1)
    order.orderNumber = maxOrder?.orderNumber ? maxOrder.orderNumber + 1 : 1;
  }
}
