import { Injectable, UnprocessableEntityException } from '@nestjs/common';
import { BaseDataService } from 'src/core/modules/data/service/base-data.service';
import { InjectRepository } from '@nestjs/typeorm';
import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants';
import { Between, In, IsNull, Like, Not, Repository } from 'typeorm';
import { QueueTicket } from '../../domain/entities/ticket.entity';
import {
  QueueItemModel,
  QueueOrderModel,
  QueueTicketModel,
} from '../models/queue.model';
import { QueueOrder } from '../../domain/entities/order.entity';
import * as moment from 'moment';
import { TransactionModel } from 'src/modules/transaction/transaction/data/models/transaction.model';

@Injectable()
export class TicketDataService extends BaseDataService<QueueTicket> {
  constructor(
    @InjectRepository(QueueTicketModel, CONNECTION_NAME.DEFAULT)
    private repo: Repository<QueueTicketModel>,

    @InjectRepository(QueueOrderModel, CONNECTION_NAME.DEFAULT)
    private order: Repository<QueueOrderModel>,

    @InjectRepository(QueueItemModel, CONNECTION_NAME.DEFAULT)
    private item: Repository<QueueItemModel>,

    @InjectRepository(TransactionModel, CONNECTION_NAME.DEFAULT)
    private transaction: Repository<TransactionModel>,
  ) {
    super(repo);
  }

  async createQueueOrder(order: QueueOrder): Promise<QueueOrderModel> {
    return await this.order.save(order);
  }

  async updateQueueTicket(ticket: QueueTicket): Promise<QueueTicketModel> {
    return await this.repo.save(ticket);
  }

  async loginQueue(id: string): Promise<QueueOrder> {
    const start = moment().startOf('day').valueOf();
    const end = moment().endOf('day').valueOf();

    const order = await this.order.findOne({
      relations: ['tickets'],
      where: [
        { transaction_id: id, date: Between(start, end) },
        { code: id, transaction_id: Not(IsNull()), date: Between(start, end) },
      ],
    });

    if (!order) {
      // Check if id is a valid UUID format
      const uuidRegex =
        /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
      const whereCondition = !uuidRegex.test(id)
        ? { invoice_code: id, booking_date: new Date() }
        : { id, booking_date: new Date() };
      const { customer_name, customer_phone } =
        await this.transaction.findOneOrFail({
          where: whereCondition,
        });

      const start = moment().startOf('day').valueOf();
      const end = moment().endOf('day').valueOf();
      const order = this.order.findOneOrFail({
        relations: ['tickets'],
        where: {
          customer: customer_name,
          phone: customer_phone,
          date: Between(start, end),
        },
      });

      return order;
    }

    return order;
  }

  async ticketByCode(code: string): Promise<QueueTicketModel[]> {
    return this.repo.find({
      where: {
        code: Like(`${code}%`),
      },
    });
  }

  async ticketByUser(user: string, phone: string): Promise<QueueTicket> {
    const start = moment().startOf('day').valueOf();
    const end = moment().endOf('day').valueOf();

    return this.repo.findOne({
      relations: ['items'],
      where: {
        customer: user,
        phone: phone,
        date: Between(start, end),
      },
    });
  }

  async loginPhone(user: string, phone: string): Promise<QueueOrder> {
    const start = moment().startOf('day').valueOf();
    const end = moment().endOf('day').valueOf();

    return this.order.findOneOrFail({
      relations: ['tickets'],
      where: {
        customer: user,
        phone: `+${phone}`,
        date: Between(start, end),
      },
    });
  }

  async orders(order_id: string): Promise<QueueOrderModel[]> {
    const order = await this.order.findOneOrFail({
      where: {
        id: order_id,
      },
    });

    if (order.transaction_id != null) {
      return this.order.find({
        where: {
          code: order.code,
        },
      });
    }

    return [order];
  }

  async transactions(transaction_id: string): Promise<QueueOrderModel[]> {
    const order = await this.order.findOneOrFail({
      where: {
        transaction_id,
      },
    });

    if (order.transaction_id != null) {
      return this.order.find({
        where: {
          code: order.code,
        },
      });
    }

    return [order];
  }

  async deleteQueue(transaction_id: string): Promise<void> {
    try {
      const transactions = await this.transactions(transaction_id);
      await this.order.remove(transactions);
    } catch (error) {
      console.log('transaction not found');
    }
  }

  async orderIds(order_id: string): Promise<string[]> {
    const orders = await this.orders(order_id);

    return orders.map((order) => order.id);
  }

  async orderItems(
    order_id: string,
    item_ids: string[],
  ): Promise<QueueOrderModel> {
    const order = await this.orderIds(order_id);
    try {
      const ticket = await this.order.findOneOrFail({
        relations: ['tickets', 'tickets.items'],
        where: {
          tickets: {
            order_id: In(order),
            items: {
              id: In(item_ids),
            },
          },
        },
      });

      return ticket;
    } catch (error) {
      throw new UnprocessableEntityException('Ticket tidak dapat ditemukan');
    }
  }

  async queuePosTickets(order_id: string): Promise<QueueTicketModel[]> {
    return this.repo.find({
      relations: [
        'items',
        'items.queue',
        'items.item',
        'items.item.item_queue',
      ],
      where: {
        order_id,
      },
    });
  }

  async queueTickets(order_id: string): Promise<QueueTicketModel[]> {
    const order = await this.orderIds(order_id);
    const start = moment().startOf('day').valueOf();
    const end = moment().endOf('day').valueOf();
    return this.repo.find({
      relations: [
        'items',
        'items.queue',
        'items.item',
        'items.item.item_queue',
      ],
      where: {
        order_id: In(order),
        date: Between(start, end),
      },
    });
  }

  async queueUniqueTickets(order_id: string): Promise<QueueTicketModel[]> {
    const start = moment().startOf('day').valueOf();
    const end = moment().endOf('day').valueOf();
    return this.repo.find({
      relations: [
        'items',
        'items.queue',
        'items.item',
        'items.item.item_queue',
      ],
      where: {
        order_id: order_id,
        date: Between(start, end),
      },
    });
  }

  async queueTicketItems(
    order_id: string,
    ticket_id: string,
  ): Promise<QueueTicketModel[]> {
    const order = await this.orderIds(order_id);
    return this.repo.find({
      relations: [
        'items',
        'items.queue',
        'items.item',
        'items.item.item_queue',
      ],
      where: {
        order_id: In(order),
        id: ticket_id,
      },
    });
  }

  async queueTicketActive(
    order_id: string,
    ticket_id: string,
  ): Promise<QueueTicketModel[]> {
    // const order = await this.orderIds(order_id);
    return this.repo.find({
      relations: ['items', 'items.queue'],
      where: {
        // order_id: In(order),
        id: ticket_id,
        // items: {
        //   queue: {
        //     status: In(['waiting']),
        //   },
        // },
      },
    });
  }

  async queueItemTickets(
    order_id: string,
    item_id: string,
  ): Promise<QueueTicketModel[]> {
    const order = await this.orderIds(order_id);
    return this.repo.find({
      relations: [
        'items',
        'items.queue',
        'items.item',
        'items.item.item_queue',
      ],
      where: {
        order_id: In(order),
        items: [{ item_id }, { item: { item_queue: { id: item_id } } }],
      },
    });
  }

  async queueItems(order_id: string): Promise<QueueItemModel[]> {
    const order = await this.orderIds(order_id);
    return this.item.find({
      relations: ['queue', 'ticket'],
      where: {
        ticket: {
          order_id: In(order),
        },
      },
    });
  }
}
