import {
  BadRequestException,
  Injectable,
  Logger,
  NotFoundException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { MoreThanOrEqual, Repository } from 'typeorm';
import { Queue } from './entities/queue.entity';
import { CreateQueueDto } from './dto/create-queue.dto';
import { UpdateQueueDto } from './dto/update-queue.dto';
import { Machine } from '@/machines/entities/machine.entity';
import { Page } from '@/pages/entities/page.entity';
import { Order } from '@/orders/entities/order.entity';
import { Employee } from '@/employees/entities/employee.entity';

@Injectable()
export class QueuesService {
  private readonly logger = new Logger(QueuesService.name);
  deleteByDate(
    dateStr: string,
  ): { deletedCount: number } | PromiseLike<{ deletedCount: number }> {
    throw new Error('Method not implemented.');
  }
  constructor(
    @InjectRepository(Queue)
    private readonly queueRepository: Repository<Queue>,

    @InjectRepository(Machine)
    private readonly machineRepository: Repository<Machine>,

    @InjectRepository(Page)
    private readonly pageRepository: Repository<Page>,

    @InjectRepository(Order)
    private readonly orderRepository: Repository<Order>,

    @InjectRepository(Employee)
    private readonly employeeRepository: Repository<Employee>,
  ) {}

  async getLatestQueuesFromDate(dateStr?: string): Promise<Queue[]> {
    this.logger.log(
      `📡 Fetching queues from ${dateStr || 'the most recent date'}`,
    );

    let startDate: Date;

    if (dateStr) {
      // ✅ ถ้ามี `dateStr` ให้ใช้เป็นวันที่เริ่มต้น
      const parts = dateStr.includes('/')
        ? dateStr.trim().split('/')
        : dateStr.trim().split('-');

      if (parts.length !== 3) {
        throw new BadRequestException(
          'Invalid date format. Use dd/MM/yyyy or yyyy-MM-dd',
        );
      }

      if (dateStr.includes('/')) {
        const [day, month, year] = parts.map(Number);
        startDate = new Date(year, month - 1, day);
      } else {
        const [year, month, day] = parts.map(Number);
        startDate = new Date(year, month - 1, day);
      }
    } else {
      // ✅ ถ้าไม่ส่ง `dateStr` มา → ใช้วันที่ล่าสุดแทน
      const latestExistingQueue = await this.queueRepository.find({
        order: { createdAt: 'DESC' },
        take: 1,
      });

      if (!latestExistingQueue.length) {
        this.logger.warn(`⚠️ No queues found in the system`);
        return [];
      }

      startDate = new Date(latestExistingQueue[0].createdAt);
    }

    startDate.setHours(0, 0, 0, 0); // ตั้งเป็น 00:00 ของวันนั้น

    this.logger.log(
      `🔍 Fetching all queues from ${startDate.toISOString()} onwards`,
    );

    return await this.queueRepository.find({
      where: { createdAt: MoreThanOrEqual(startDate) },
      order: { createdAt: 'ASC' }, // เรียงจากเก่าสุดไปใหม่สุด
    });
  }

  async create(createQueueDto: CreateQueueDto) {
    const { MachineID, PageID, OrderID, EmployeeIds, ...queueData } =
      createQueueDto;

    // console.log('📌 Received CreateQueueDto:', createQueueDto);

    // 🔍 Fetch related entities
    const machine = await this.machineRepository.findOne({
      where: { MachineID },
    });
    if (!machine)
      throw new NotFoundException(`Machine with ID ${MachineID} not found`);

    const page = await this.pageRepository.findOne({ where: { PageID } });
    if (!page) throw new NotFoundException(`Page with ID ${PageID} not found`);

    const order = await this.orderRepository.findOne({ where: { OrderID } });
    if (!order)
      throw new NotFoundException(`Order with ID ${OrderID} not found`);

    const employees = await this.employeeRepository.findByIds(EmployeeIds);
    if (employees.length !== EmployeeIds.length) {
      throw new NotFoundException(`One or more employees not found`);
    }

    // console.log('✅ Related Entities Fetched:');
    // console.log('   - Machine:', machine);
    // console.log('   - Page:', page);
    // console.log('   - Order:', order);
    // console.log('   - Employees:', employees);

    // ✅ Create Queue entity and save to DB
    const newQueue = this.queueRepository.create({
      ...queueData,
      machine,
      page,
      order,
      employees,
    });

    // console.log('🛠️ Preparing to save new Queue:', newQueue);

    const savedQueue = await this.queueRepository.save(newQueue);

    // console.log('✅ Queue saved successfully:', savedQueue);

    // ✅ Return the response with full details
    return {
      message: 'Queue created successfully',
      queue: savedQueue,
    };
  }

  async createMultiple(createQueueDtos: CreateQueueDto[]) {
    const newQueues = [];

    if (!Array.isArray(createQueueDtos)) {
      throw new BadRequestException('Request body must be an array of queues');
    }
    for (const createQueueDto of createQueueDtos) {
      const { MachineID, PageID, OrderID, EmployeeIds, ...queueData } =
        createQueueDto;

      // console.log('👉 Raw EmployeeIds:', EmployeeIds);

      // ✅ ตรวจสอบว่า `EmployeeIds` เป็น Array ที่ไม่ว่าง
      if (!Array.isArray(EmployeeIds) || EmployeeIds.length === 0) {
        throw new BadRequestException(`EmployeeIds must be a non-empty array`);
      }

      // ✅ แปลงค่าให้เป็นตัวเลข และกรองค่าที่ไม่ถูกต้อง
      const validEmployeeIds = EmployeeIds.map((id) => Number(id)).filter(
        (id) => !isNaN(id) && id > 0,
      );

      // console.log('✅ Valid EmployeeIds:', validEmployeeIds);

      if (validEmployeeIds.length !== EmployeeIds.length) {
        throw new BadRequestException(
          `Invalid EmployeeIds provided: ${EmployeeIds}`,
        );
      }

      // ✅ ตรวจสอบและแปลง `MachineID`, `OrderID`, `PageID`
      const machineId = Number(MachineID);
      if (isNaN(machineId))
        throw new BadRequestException(`Invalid MachineID: ${MachineID}`);

      const orderId = Number(OrderID);
      if (isNaN(orderId))
        throw new BadRequestException(`Invalid OrderID: ${OrderID}`);

      let pageId: number | null = null;
      if (PageID !== null && PageID !== undefined) {
        pageId = Number(PageID);
        if (isNaN(pageId))
          throw new BadRequestException(`Invalid PageID: ${PageID}`);
      }

      // 🔍 ค้นหา Entity ที่เกี่ยวข้อง
      const machine = await this.machineRepository.findOne({
        where: { MachineID: machineId },
      });
      if (!machine)
        throw new NotFoundException(`Machine with ID ${machineId} not found`);

      const page =
        pageId !== null
          ? await this.pageRepository.findOne({ where: { PageID: pageId } })
          : null;
      if (pageId !== null && !page)
        throw new NotFoundException(`Page with ID ${pageId} not found`);

      const order = await this.orderRepository.findOne({
        where: { OrderID: orderId },
      });
      if (!order)
        throw new NotFoundException(`Order with ID ${orderId} not found`);

      // 🔍 ค้นหา Employees ที่มีอยู่ในฐานข้อมูล
      const employees =
        await this.employeeRepository.findByIds(validEmployeeIds);
      const foundIds = employees.map((e) => e.EmployeeID);
      const missingIds = validEmployeeIds.filter(
        (id) => !foundIds.includes(id),
      );

      if (missingIds.length > 0) {
        console.error(`❌ Missing EmployeeIDs: ${missingIds.join(', ')}`);
        throw new NotFoundException(
          `Employees not found: ${missingIds.join(', ')}`,
        );
      }

      // ✅ สร้าง Queue object
      const newQueue = this.queueRepository.create({
        ...queueData,
        machine,
        page,
        order,
        employees,
      });

      newQueues.push(newQueue);
    }

    // ✅ บันทึกทั้งหมดพร้อมกัน
    const savedQueues = await this.queueRepository.save(newQueues);

    return {
      message: `${savedQueues.length} queues created successfully`,
      queues: savedQueues,
    };
  }

  // ✅ Fetch all Queues with relationships
  async findAll() {
    return await this.queueRepository.find({
      relations: ['machine', 'page', 'order', 'employees'],
    });
  }

  // ✅ Fetch a single Queue by ID
  async findOne(id: number) {
    const queue = await this.queueRepository.findOne({
      where: { QueueID: id },
      relations: ['machine', 'page', 'order', 'employees'],
    });

    if (!queue) {
      throw new NotFoundException(`Queue with ID ${id} not found`);
    }

    return queue;
  }

  // ✅ Update Queue
  async update(id: number, updateQueueDto: UpdateQueueDto) {
    try {
      console.log('🔄 Updating Queue ID:', id, updateQueueDto);

      const {
        MachineID,
        PageID,
        OrderID,
        EmployeeIds,
        startTime,
        finishTime,
        ...queueData
      } = updateQueueDto;

      // 🔍 ค้นหา Queue ที่จะอัปเดต
      const existingQueue = await this.queueRepository.findOne({
        where: { QueueID: id },
        relations: ['machine', 'page', 'order', 'employees'],
      });

      if (!existingQueue) {
        throw new NotFoundException(`Queue with ID ${id} not found`);
      }

      // 🔍 Fetch entities if IDs are provided
      const machine = MachineID
        ? await this.machineRepository.findOne({ where: { MachineID } })
        : existingQueue.machine;

      if (MachineID && !machine)
        throw new NotFoundException(`Machine with ID ${MachineID} not found`);

      const page = PageID
        ? await this.pageRepository.findOne({ where: { PageID } })
        : existingQueue.page;

      if (PageID && !page)
        throw new NotFoundException(`Page with ID ${PageID} not found`);

      const order = OrderID
        ? await this.orderRepository.findOne({ where: { OrderID } })
        : existingQueue.order;

      if (OrderID && !order)
        throw new NotFoundException(`Order with ID ${OrderID} not found`);

      // 🔍 ค้นหา Employees และคำนวณการเชื่อมโยง
      let employees = existingQueue.employees;
      if (EmployeeIds) {
        const fetchedEmployees =
          await this.employeeRepository.findByIds(EmployeeIds);
        if (fetchedEmployees.length !== EmployeeIds.length) {
          throw new NotFoundException(`One or more employees not found`);
        }
        employees = fetchedEmployees;
      }

      // ✅ Convert timestamps from string to Date
      const updateData: Partial<Queue> = {
        ...queueData,
        machine,
        page,
        order,
        employees,
        startTime: startTime ? new Date(startTime) : existingQueue.startTime,
        finishTime: finishTime
          ? new Date(finishTime)
          : existingQueue.finishTime,
      };

      // ✅ ใช้ `save()` แทน `update()` เพื่อรองรับ Many-to-Many (employees)
      const updatedQueue = await this.queueRepository.save({
        ...existingQueue,
        ...updateData,
      });

      console.log('✅ Queue updated successfully:', updatedQueue);

      return updatedQueue;
    } catch (error) {
      console.error('❌ Error in updateQueue:', error);
      throw new Error('Database update failed');
    }
  }

  // ✅ Delete Queue
  async remove(id: number) {
    const queue = await this.queueRepository.findOne({
      where: { QueueID: id },
      relations: ['machine', 'page', 'order', 'employees'],
    });

    if (!queue) {
      throw new NotFoundException(`Queue with ID ${id} not found`);
    }

    await this.queueRepository.delete(id);

    return { message: 'Queue deleted successfully', deletedQueue: queue };
  }

  async deleteNewlyCreatedQueuesFromDate(
    dateStr: string,
  ): Promise<{ deletedCount: number }> {
    this.logger.log(`🔄 Deleting newly created queues from date: ${dateStr}`);

    if (!dateStr) throw new BadRequestException('dateStr is required');

    // ✅ แปลง string (dd/MM/yyyy หรือ yyyy-MM-dd) → Date object
    let date: Date;
    const parts = dateStr.includes('/')
      ? dateStr.trim().split('/')
      : dateStr.trim().split('-');

    if (parts.length !== 3) {
      throw new BadRequestException(
        'Invalid date format. Use dd/MM/yyyy or yyyy-MM-dd',
      );
    }

    if (dateStr.includes('/')) {
      const [day, month, year] = parts.map(Number);
      date = new Date(year, month - 1, day);
    } else {
      const [year, month, day] = parts.map(Number);
      date = new Date(year, month - 1, day);
    }

    const startOfDay = new Date(date.setHours(0, 0, 0, 0));
    const now = new Date();

    // ✅ ค้นหา Queue ที่ตรงเงื่อนไข
    const queuesToDelete = await this.queueRepository.find({
      where: {
        startTime: MoreThanOrEqual(startOfDay),
        createdAt: MoreThanOrEqual(startOfDay),
      },
    });

    if (queuesToDelete.length === 0) {
      this.logger.warn(`⚠️ No queues matched deletion criteria for ${dateStr}`);
      return { deletedCount: 0 };
    }

    // ✅ Log รายละเอียด queue ที่จะลบ
    this.logger.log(
      `📌 Queues to be deleted (${queuesToDelete.length} items):`,
    );
    queuesToDelete.forEach((queue) => {
      this.logger.log(
        `🗑️ QueueID: ${queue.QueueID}, startTime: ${queue.startTime}, createdAt: ${queue.createdAt}`,
      );
    });

    // ✅ ลบ queue
    await this.queueRepository.remove(queuesToDelete);

    this.logger.log(
      `✅ Successfully deleted ${queuesToDelete.length} queue(s).`,
    );
    return { deletedCount: queuesToDelete.length };
  }
}