import {
  BadRequestException,
  Injectable,
  NotFoundException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { DevicesEntity } from 'src/entities/Devices.entity';
import { DeviceAssignmentsEntity } from 'src/entities/DeviceAssignments.entity';
import { UserEntity } from 'src/entities/User.entity';
import { Repository } from 'typeorm';
import { CreateAssignmentDto } from './dtos/CreateAssignmentDto.dto';
import { formatDate } from 'src/utils/common/global-utility';
import {
  getSkipOffset,
  paginate,
} from 'src/config/pagination/pagination.service';
import { Response } from 'src/config/pagination/paginationDto';

@Injectable()
export class DeviceAssignmentsService {
  constructor(
    @InjectRepository(DeviceAssignmentsEntity)
    private assignmentRepo: Repository<DeviceAssignmentsEntity>,
    @InjectRepository(DevicesEntity)
    private deviceRepo: Repository<DevicesEntity>,
    @InjectRepository(UserEntity)
    private userRepo: Repository<UserEntity>,
  ) {}

  async assign(dto: any, assignedById: number) {
    const device = await this.deviceRepo.findOne({
      where: { id: dto.device_id },
    });
    // console.log({device})
    if (!device || device.status !== 'available')
      throw new BadRequestException('Device not available');

    const employee = await this.userRepo.findOne({
      where: { id: dto.employee_id },
    });
    if (!employee) throw new NotFoundException('Employee not found');

    const assignedBy = await this.userRepo.findOne({
      where: { id: assignedById },
    });

    const assignment = this.assignmentRepo.create({
      ...dto,
      assigned_date: formatDate(dto.assigned_date, 'YYYY-MM-DD'),
      expected_return_date: formatDate(dto.expected_return_date, 'YYYY-MM-DD'),
      actual_return_date: formatDate(dto.actual_return_date, 'YYYY-MM-DD'),
    });

    device.status = 'assigned';

    await this.deviceRepo.save(device);
    return this.assignmentRepo.save(assignment);
  }

  async returnDevice(id: number) {
    const assignment = await this.assignmentRepo.findOne({
      where: { id },
      relations: ['device'],
    });

    if (!assignment || assignment.status !== 'active')
      throw new BadRequestException('Invalid assignment');

    assignment.status = 'returned';
    // assignment.actualReturnDate = new Date();
    assignment.device.status = 'available';

    await this.deviceRepo.save(assignment.device);
    return this.assignmentRepo.save(assignment);
  }

  async findAll(searchDto: any): Promise<Response<any>> {
    const query = this.assignmentRepo
      .createQueryBuilder('assignment')
      .leftJoinAndSelect('assignment.device', 'device')
      .leftJoinAndSelect('assignment.employee', 'employee')
      .orderBy('assignment.id', 'DESC');

    /**
     * 🔍 Text Search (device + employee)
     */
    // query.where('device.id = 16')
    if (searchDto.filterBy) {
      if (searchDto.filterBy === 'employee') {
        query.where('employee.id = :id', { id: searchDto.id });
      } else if (searchDto.filterBy === 'device') {
        query.where('device.id = :id', { id: searchDto.id });
      }
    }
    if (searchDto.search) {
      query.andWhere(
        `(
          device.name LIKE :search OR 
          device.serial_number LIKE :search OR 
          employee.first_name LIKE :search OR 
          employee.last_name LIKE :search OR 
          employee.employeeCode LIKE :search
        )`,
        {
          search: `%${searchDto.search}%`,
        },
      );
    }

    /**
     * 📱 Filter by Device
     */
    if (searchDto.device_ids?.length > 0) {
      query.andWhere('device.id IN (:...device_ids)', {
        device_ids: searchDto.device_ids,
      });
    }

    /**
     * 👨‍💼 Filter by Employee
     */
    if (searchDto.employee_ids?.length > 0) {
      query.andWhere('employee.id IN (:...employee_ids)', {
        employee_ids: searchDto.employee_ids,
      });
    }

    /**
     * 📌 Status Filter
     */
    if (searchDto.status?.length > 0) {
      query.andWhere('assignment.status IN (:...status)', {
        status: searchDto.status,
      });
    }

    /**
     * 📅 Assignment Date Range
     */
    if (searchDto.assigned_on) {
      const { from, to } = searchDto.assigned_on;

      if (from && to) {
        query.andWhere('assignment.assigned_at BETWEEN :from AND :to', {
          from,
          to,
        });
      } else if (from) {
        query.andWhere('assignment.assigned_at >= :from', { from });
      } else if (to) {
        query.andWhere('assignment.assigned_at <= :to', { to });
      }
    }

    /**
     * 📅 Return Date Range
     */
    if (searchDto.returned_on) {
      const { from, to } = searchDto.returned_on;

      if (from && to) {
        query.andWhere('assignment.returned_at BETWEEN :rfrom AND :rto', {
          rfrom: from,
          rto: to,
        });
      } else if (from) {
        query.andWhere('assignment.returned_at >= :rfrom', {
          rfrom: from,
        });
      } else if (to) {
        query.andWhere('assignment.returned_at <= :rto', {
          rto: to,
        });
      }
    }

    /**
     * 👤 Created By
     */
    if (searchDto.created_by?.length > 0) {
      query.andWhere('assignment.created_by IN (:...created_by)', {
        created_by: searchDto.created_by,
      });
    }

    /**
     * ✏️ Updated By
     */
    if (searchDto.updated_by?.length > 0) {
      query.andWhere('assignment.modified_by IN (:...updated_by)', {
        updated_by: searchDto.updated_by,
      });
    }

    /**
     * ✅ Active Filter
     */
    if (searchDto.active?.length > 0) {
      query.andWhere('assignment.active IN (:...active)', {
        active: searchDto.active,
      });
    }

    /**
     * 📅 Created Date Range
     */
    if (searchDto.created_on) {
      const { from, to } = searchDto.created_on;

      if (from && to) {
        query.andWhere('assignment.created BETWEEN :cfrom AND :cto', {
          cfrom: from,
          cto: to,
        });
      } else if (from) {
        query.andWhere('assignment.created >= :cfrom', { cfrom: from });
      } else if (to) {
        query.andWhere('assignment.created <= :cto', { cto: to });
      }
    }

    /**
     * 📅 Updated Date Range
     */
    if (searchDto.updated_on) {
      const { from, to } = searchDto.updated_on;

      if (from && to) {
        query.andWhere('assignment.modified BETWEEN :ufrom AND :uto', {
          ufrom: from,
          uto: to,
        });
      } else if (from) {
        query.andWhere('assignment.modified >= :ufrom', {
          ufrom: from,
        });
      } else if (to) {
        query.andWhere('assignment.modified <= :uto', {
          uto: to,
        });
      }
    }

    /**
     * 📄 Pagination
     */
    const [skip, limit] = getSkipOffset(searchDto);

    const [data, total] = await query.skip(skip).take(limit).getManyAndCount();

    const pagination = paginate(total, searchDto.per_page, searchDto.page);

    return {
      list: data,
      pagination,
    };
  }
}
