import { Injectable } from '@nestjs/common';
import * as moment from 'moment';
import { TABLE_NAME } from 'src/core/strings/constants/table.constants';
import { TransactionUserType } from 'src/modules/transaction/transaction/constants';
import { TransactionItemModel } from 'src/modules/transaction/transaction/data/models/transaction-item.model';
import { QueueBucketReadService } from '../../data/services/queue-bucket';
import { QueueService } from '../../data/services/queue.service';
import { TicketDataService } from '../../data/services/ticket.service';
import { RegisterQueueDto } from '../../infrastructure/controllers/dto/register-queue.dto';
import { QueueOrder } from '../entities/order.entity';
import { QueueItem } from '../entities/queue-item.entity';
import { QueueTicket } from '../entities/ticket.entity';
import { QueueTimeFormula } from './formula/queue-time.formula';
import { RegisterQueueManager } from './register-queue.manager';
import { TransactionModel } from 'src/modules/transaction/transaction/data/models/transaction.model';
import { QueueModel } from '../../data/models/queue.model';

@Injectable()
export class GenerateQueueManager {
  constructor(
    private readonly ticketService: TicketDataService,
    private readonly queueService: QueueService,
    private readonly bucketService: QueueBucketReadService,
    private readonly queueTimeFormula: QueueTimeFormula,
  ) {}

  async generate(transaction: TransactionModel) {
    const date = transaction.booking_date ?? transaction.invoice_date;
    const queue_date = moment(date, 'YYYY-MM-DD').unix();
    const isToday = moment(date, 'YYYY-MM-DD').isSame(moment(), 'day');

    const { id, customer_name, customer_phone, invoice_code } = transaction;

    const customerOrder = {
      code: invoice_code,
      customer: customer_name,
      phone: customer_phone,
      date: queue_date * 1000,
      transaction_id: id,
    };

    const items = this.generateItems(transaction.items);

    const existTicket = isToday
      ? await this.ticketService.ticketByUser(customer_name, customer_phone)
      : null;

    const insertTickets: QueueTicket[] = [];
    if (customer_name && customer_phone && existTicket) {
      existTicket.items.push(...items);
      await this.ticketService.updateQueueTicket(existTicket);

      existTicket.items = items;
      insertTickets.push(existTicket);
    } else {
      const ticket: QueueTicket = { ...customerOrder, items };
      const order: QueueOrder = { ...customerOrder, tickets: [ticket] };

      const queueOrder = await this.ticketService.createQueueOrder(order);
      insertTickets.push(...queueOrder.tickets);
    }

    const results: QueueModel[] = [];
    const vipCustomer =
      transaction.customer_category?.has_vip_pass ||
      transaction.customer_type === TransactionUserType.VIP;
    if (vipCustomer || transaction.creator_counter_no != null) {
      for (const ticket of insertTickets) {
        const ticket_id = ticket.id;
        const items = {};

        ticket.items.forEach((item) => {
          const item_id = item['item_queue_id'];
          const currentItem = items[item_id];

          if (currentItem) {
            currentItem.qty += item.qty;
          }
          items[item_id] = currentItem
            ? currentItem
            : {
                item_id,
                ticket_id,
                qty: item.qty,
              };
        });

        for (const payload of Object.values(items)) {
          const isVip = vipCustomer || transaction.creator_counter_no == null;
          const result = await this.create(payload as any, isVip);
          results.push(result);
        }
      }
    }
    return results;
  }

  generateItems(items: TransactionItemModel[]): QueueItem[] {
    const transactionItems = [];

    items.forEach((item) => {
      if (item.item.use_queue) {
        transactionItems.push({
          item_queue_id: item.item.item_queue_id,
          item_id: item.item_id,
          qty: item.qty,
        });
      }

      if (item.item.bundling_items.length > 0) {
        item.item.bundling_items.forEach((bundling_item) => {
          if (bundling_item.use_queue) {
            transactionItems.push({
              item_queue_id: bundling_item.item_queue_id,
              item_id: bundling_item.id,
              qty: item.qty,
            });
          }
        });
      }
    });

    return this.mergeQueueItems(transactionItems);
  }

  mergeQueueItems(arr) {
    const result = {};
    arr.forEach((item) => {
      if (result[item.item_id]) {
        result[item.item_id].qty += item.qty;
      } else {
        result[item.item_id] = { ...item };
      }
    });
    return Object.values<QueueItem>(result);
  }

  async create(data: RegisterQueueDto, isVip = false): Promise<QueueModel> {
    const queue = await this.queueService.getTicketItems(
      data.ticket_id,
      data.item_id,
    );
    const queueRequest: any = {
      qty: data.qty,
      item_id: queue.id,
      vip: isVip,
    };
    const registerQueueManager = new RegisterQueueManager(
      this.bucketService,
      this.queueTimeFormula,
    );
    registerQueueManager.setData(queueRequest);
    registerQueueManager.setService(this.queueService, TABLE_NAME.QUEUE);
    await registerQueueManager.execute();

    const result = await registerQueueManager.getResult();
    result.time = null;

    return result;
  }
}
