import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { DevicesEntity } from 'src/entities/Devices.entity';
import { VendorsEntity } from 'src/entities/Vendors.entity';
import { Repository } from 'typeorm';
import { CreateDeviceDto } from './dtos/CreateDeviceDto.dto';
import {
  getSkipOffset,
  paginate,
} from 'src/config/pagination/pagination.service';
import { Response } from 'src/config/pagination/paginationDto';
import { plainToInstance } from 'class-transformer';
import { AddAttachmentDto } from '../visa-commission/dto/add-attachment.dto';
import { FileService } from 'src/services/file.service';
import { RemoveAttachmentDto } from '../visa-commission/dto/remove-attachment.dto';

@Injectable()
export class DevicesService {
  constructor(
    private fileService: FileService,
    @InjectRepository(DevicesEntity) private repo: Repository<DevicesEntity>,
    @InjectRepository(VendorsEntity)
    private vendorRepo: Repository<VendorsEntity>,
  ) {}

  async getVendorDeviceChart() {
    const result = await this.repo
      .createQueryBuilder('device')
      .leftJoin('device.vendor', 'vendor')
      .select('vendor.name', 'name')
      .addSelect('COUNT(device.id)', 'devices')
      .groupBy('vendor.id')
      .addGroupBy('vendor.name')
      .getRawMany();
  
    return result.map((item) => ({
      name: item.name || 'Unknown Vendor',
      devices: Number(item.devices),
    }));
  }

  async getDeviceStatusChart() {
    const result = await this.repo
      .createQueryBuilder('device')
      .select(
        `SUM(CASE WHEN device.status = 'assigned' THEN 1 ELSE 0 END)`,
        'assigned',
      )
      .addSelect(
        `SUM(CASE WHEN device.status = 'available' THEN 1 ELSE 0 END)`,
        'available',
      )
      .addSelect(
        `SUM(CASE WHEN device.status = 'maintenance' THEN 1 ELSE 0 END)`,
        'maintenance',
      )
      .addSelect(
        `SUM(CASE WHEN device.status = 'retired' THEN 1 ELSE 0 END)`,
        'retired',
      )
      .getRawOne();
  
    return [
      { name: 'Assigned', value: Number(result.assigned) },
      { name: 'Available', value: Number(result.available) },
      { name: 'Maintenance', value: Number(result.maintenance) },
      { name: 'Retired', value: Number(result.retired) }, // or map as "Lost" if needed
    ];
  }

  async getTotalDevices(): Promise<number> {
    return await this.repo.count();
  }

  async create(dto: any) {
    const vendor = dto.vendor_id
      ? await this.vendorRepo.findOne({ where: { id: dto.vendor_id } })
      : null;

    const device = this.repo.create({ ...dto, vendor });
    return this.repo.save(device);
  }

  // findAll() {
  //   return this.repo.find({ relations: ["vendor"] });
  // }

  async edit(id: any, updateVendorDto: any): Promise<any> {
    const vendor = await this.findOne(id);
    this.repo.merge(vendor, updateVendorDto);
    return await this.repo.save(vendor);
  }

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

    if (searchDto.search_title) {
      query.andWhere(
        `(device.asset_tag LIKE :search OR device.serial_number LIKE :search OR device.brand LIKE :search OR device.model LIKE :search OR device.device_condition LIKE :search OR device.notes LIKE :search)`,
        {
          search: `%${searchDto.search_title}%`,
        },
      );
    }

    if (searchDto.vendors && searchDto.vendors.length > 0) {
      query.andWhere('vendor.id IN (:...vendors)', {
        vendors: searchDto.vendors,
      });
    }

    if (searchDto.status && searchDto.status.length > 0) {
      query.andWhere('device.status IN (:...status)', {
        status: searchDto.status,
      });
    }

    if (searchDto.device_type && searchDto.device_type.length > 0) {
      query.andWhere('device.device_type IN (:...device_type)', {
        device_type: searchDto.device_type,
      });
    }

    if (searchDto.created_by && searchDto.created_by.length > 0) {
      query.andWhere('vendor.created_by IN (:...created_by)', {
        created_by: searchDto.created_by,
      });
    }

    if (searchDto.updated_by && searchDto.updated_by.length > 0) {
      query.andWhere('vendor.modified_by IN (:...updated_by)', {
        updated_by: searchDto.updated_by,
      });
    }

    if (searchDto.lease_start_date) {
      const { from, to } = searchDto.lease_start_date;

      if (from && to) {
        query.andWhere(
          'device.lease_start_date BETWEEN :updatedFrom AND :updatedTo',
          {
            updatedFrom: new Date(from),
            updatedTo: new Date(to),
          },
        );
      } else if (from) {
        query.andWhere('device.lease_start_date >= :updatedFrom', {
          updatedFrom: new Date(from),
        });
      } else if (to) {
        query.andWhere('device.lease_start_date <= :updatedTo', {
          updatedTo: new Date(to),
        });
      }
    }

    if (searchDto.lease_end_date) {
      const { from, to } = searchDto.lease_end_date;

      if (from && to) {
        query.andWhere(
          'device.lease_end_date BETWEEN :updatedFrom AND :updatedTo',
          {
            updatedFrom: new Date(from),
            updatedTo: new Date(to),
          },
        );
      } else if (from) {
        query.andWhere('device.lease_end_date >= :updatedFrom', {
          updatedFrom: new Date(from),
        });
      } else if (to) {
        query.andWhere('device.lease_end_date <= :updatedTo', {
          updatedTo: new Date(to),
        });
      }
    }

    if (searchDto.created_on) {
      const { from, to } = searchDto.created_on;

      if (from && to) {
        query.andWhere(
          'device.created_at BETWEEN :createdFrom AND :createdTo',
          {
            createdFrom: from,
            createdTo: to,
          },
        );
      } else if (from) {
        query.andWhere('device.created_at >= :createdFrom', {
          createdFrom: from,
        });
      } else if (to) {
        query.andWhere('device.created_at <= :createdTo', {
          createdTo: to,
        });
      }
    }

    if (searchDto.updated_on) {
      const { from, to } = searchDto.updated_on;

      if (from && to) {
        query.andWhere(
          'device.updated_at BETWEEN :updatedFrom AND :updatedTo',
          {
            updatedFrom: from,
            updatedTo: to,
          },
        );
      } else if (from) {
        query.andWhere('device.updated_at >= :updatedFrom', {
          updatedFrom: from,
        });
      } else if (to) {
        query.andWhere('device.updated_at <= :updatedTo', {
          updatedTo: to,
        });
      }
    }

    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,
    };
  }

  findOne(id: number) {
    return this.repo.findOne({ where: { id }, relations: ['vendor'] });
  }

  async update(id: number, dto: any) {
    await this.repo.update(id, dto);
    return this.findOne(id);
  }

  remove(id: number) {
    return this.repo.delete(id);
  }

  /**
   * addAttachments - Adds new file attachments to an existing bonus receipt
   *
   * @param params - Contains bonus receipt ID and new file attachments
   * @param userData - User data for audit trail
   * @returns Original parameters (for confirmation)
   */
  async getAttachments(id: string): Promise<any> {
    return await this.repo
      .createQueryBuilder('main')
      .select(['main.id', 'main.attachment_file'])
      .where('main.id = :id', { id })
      .orderBy('main.id', 'DESC')
      .getOne();
  }

  async addAttachments(params: AddAttachmentDto, userData: any): Promise<any> {
    let repository;
    repository = this.repo;

    const record = await repository
      .createQueryBuilder()
      .where('id = :id', { id: params.id })
      .getOne();

    const updatedAttachments = record.attachment_file
      ? [...record.attachment_file, ...params.attachment_file]
      : params.attachment_file;

    if (updatedAttachments.length) {
      const entity = plainToInstance(DevicesEntity, {
        attachment_file: updatedAttachments,
        // modified_by_user: userData.id,
      });

      await repository.update({ id: params.id }, entity);
    }

    return params;
  }

  async removeAttachments(
    params: RemoveAttachmentDto,
    userData: any,
  ): Promise<any> {
    let repository;
    repository = this.repo;

    const record = await repository
      .createQueryBuilder()
      // .where('id = :id', { id: params.row_id })
      .where('id = :id', { id: 1 })
      .getOne();

    if (!record?.attachment_file?.length) return params;

    const updatedAttachments = record.attachment_file.filter(
      (file: string) => file !== params.file_name,
    );

    if (updatedAttachments.length !== record.attachment_file.length) {
      const entity = plainToInstance(DevicesEntity, {
        attachment_file: updatedAttachments,
        modified_by_user: userData.id,
      });

      await repository.update({ id: 1 }, entity);
      await this.fileService.removeFile(params.file_name);
    }

    return params;
  }
}
