import {
  BadRequestException,
  Injectable,
  NotFoundException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Equipo } from './entities/equipo.entity';
import { UpdateEquipoDto } from './dto/update-equipo.dto';
import axios from 'axios';
import e, { Response } from 'express';
import * as ExcelJS from 'exceljs';

import {
  Adscripcion,
  Estado,
  Laboratorio,
  Marca,
  Periferico,
  Procesador,
  Proyecto,
  SistemaOperativo,
  TipoEquipo,
  Uso,
} from './entities/catalogo.entities';

import { CreateEquipoDto } from './dto/create-equipo.dto';

import { MovimientoService } from 'src/movimiento/movimiento.service';
import { Cron } from '@nestjs/schedule';
import { FiltrosGraficaDto, FiltrosTablaDto } from './dto/grafica.dto';

@Injectable()
export class EquipoService {
  constructor(
    private readonly movimiento: MovimientoService,

    @InjectRepository(Equipo)
    private readonly equipoRepository: Repository<Equipo>,
    @InjectRepository(Uso)
    private readonly usoRepo: Repository<Uso>,

    @InjectRepository(Marca)
    private readonly marcaRepo: Repository<Marca>,

    @InjectRepository(Estado)
    private readonly estadoRepo: Repository<Estado>,

    @InjectRepository(Adscripcion)
    private readonly adscripcionRepo: Repository<Adscripcion>,

    @InjectRepository(TipoEquipo)
    private readonly tipoEquipoRepo: Repository<TipoEquipo>,

    @InjectRepository(SistemaOperativo)
    private readonly sistemaOperativoRepo: Repository<SistemaOperativo>,

    @InjectRepository(Procesador)
    private readonly procesadorRepo: Repository<Procesador>,

    @InjectRepository(Periferico)
    private readonly perifericoRepo: Repository<Periferico>,
    @InjectRepository(Laboratorio)
    private readonly laboratorioRepo: Repository<Laboratorio>,
    @InjectRepository(Proyecto)
    private readonly proyectoRepo: Repository<Proyecto>,
  ) { }

  async deleteEquipo(inventario: string): Promise<{ message: string }> {
    const equipo = await this.equipoRepository.findOne({
      where: { inventario },
    });

    if (!equipo) {
      throw new NotFoundException(
        `No se encontró el equipo con id ${inventario}`,
      );
    }

    await this.equipoRepository.remove(equipo);

    return {
      message: `Equipo con inv ${inventario} eliminado correctamente`,
    };
  }

  async buscarEquipos(filtros: any, page: number = 1, limit: number = 10) {
    const query = this.equipoRepository
      .createQueryBuilder('equipo')
      .leftJoinAndSelect('equipo.marca', 'marca')
      .leftJoinAndSelect('equipo.estado', 'estado')
      .leftJoinAndSelect('equipo.adscripcion', 'adscripcion')
      .leftJoinAndSelect('equipo.tipoEquipo', 'tipoEquipo')
      .leftJoinAndSelect('equipo.sistemaOperativo', 'sistemaOperativo')
      .leftJoinAndSelect('equipo.procesador', 'procesador')
      .leftJoinAndSelect('equipo.tipoUso', 'tipoUso');

    // Filtros dinámicos
    if (filtros.inventario) {
      query.andWhere('equipo.inventario LIKE :inventario', {
        inventario: `%${filtros.inventario}%`,
      });
    }

    if (filtros.tipoEquipo) {
      query.andWhere('tipoEquipo.id = :tipoEquipo', {
        tipoEquipo: filtros.tipoEquipo,
      });
    }

    if (filtros.antiguedad) {
      query.andWhere('equipo.antiguedad LIKE :antiguedad', {
        antiguedad: `%${filtros.antiguedad}%`,
      });
    }

    if (filtros.marca) {
      query.andWhere('marca.id = :marca', { marca: filtros.marca });
    }

    if (filtros.estado) {
      query.andWhere('estado.id = :estado', { estado: filtros.estado });
    }

    if (filtros.adscripcion) {
      query.andWhere('adscripcion.id = :adscripcion', {
        adscripcion: filtros.adscripcion,
      });
    }

    if (filtros.tipoUso) {
      query.andWhere('tipoUso.id = :tipoUso', { tipoUso: filtros.tipoUso });
    }

    if (filtros.procesador) {
      query.andWhere('procesador.id = :procesador', {
        procesador: filtros.procesador,
      });
    }

    if (filtros.sistemaOperativo) {
      query.andWhere('sistemaOperativo.id = :sistemaOperativo', {
        sistemaOperativo: filtros.sistemaOperativo,
      });
    }

    // Paginación
    query.skip((page - 1) * limit).take(limit);

    // Ejecutar consulta
    const [data, total] = await query.getManyAndCount();

    return {
      total,
      page,
      limit,
      data,
    };
  }
  async updateEquipo(equipoNuevo: UpdateEquipoDto, id_equipo: number, id_user) {
    let equipo = await this.equipoRepository.findOne({
      where: { id_equipo },
      relations: ['tipoEquipo'],
    });
    if (!equipo) {
      throw new NotFoundException('No se encontó equipo');
    }

    const updateData = {
      id_equipo,
      lugar: equipoNuevo.lugar ? equipoNuevo.lugar : undefined,
      serie: equipoNuevo.serie ? equipoNuevo.serie : undefined,
      modelo: equipoNuevo.modelo ? equipoNuevo.modelo : undefined,
      estado: equipoNuevo.id_estado
        ? { id_estado: equipoNuevo.id_estado }
        : undefined,
      adscripcion: equipoNuevo.id_adscripcion
        ? { id_adscripcion: equipoNuevo.id_adscripcion }
        : undefined,
      sistemaOperativo: equipoNuevo.id_sistema_operativo
        ? { id_sistema_operativo: equipoNuevo.id_sistema_operativo }
        : undefined,
      procesador: equipoNuevo.id_procesador
        ? { id_procesador: equipoNuevo.id_procesador }
        : undefined,
      tipoUso: equipoNuevo.id_tipo_uso
        ? { id_uso: equipoNuevo.id_tipo_uso }
        : undefined,
      marca: equipoNuevo.id_marca
        ? { id_marca: equipoNuevo.id_marca }
        : undefined,
      periferico: equipoNuevo.id_periferico
        ? { id_periferico: equipoNuevo.id_periferico }
        : undefined,
      laboratorio: equipoNuevo.id_laboratorio
        ? { id_laboratorio: equipoNuevo.id_laboratorio }
        : undefined,
      proyecto: equipoNuevo.id_proyecto
        ? { id_proyecto: equipoNuevo.id_proyecto }
        : undefined,
    };

    const equipoUpdate = await this.equipoRepository.preload(updateData);

    if (!equipoUpdate) {
      throw new Error(`No se encontró el equipo con id ${id_equipo}`);
    }
    const mov = await this.movimiento.crear({
      idUsuario: id_user,
      idEquipo: id_equipo,
      observaciones: equipoNuevo.observaciones,
    });
    return await this.equipoRepository.save(equipoUpdate);
  }

  async findEquipo(inventario: string) {
    const equipo = await this.equipoRepository.findOne({
      where: { inventario },
      relations: [
        'marca',
        'estado',
        'adscripcion',
        'tipoEquipo',
        'sistemaOperativo',
        'procesador',
        'tipoUso',
        'periferico',
        'proyecto',
        'laboratorio',
      ],
    });

    if (!equipo) {
      throw new NotFoundException(
        `No se encontró el equipo con inventario ${inventario}`,
      );
    }

    const responsable = await this.searchResponsable(
      equipo.adscripcion.adscripcion,
    );

    const observaciones = await this.movimiento.buscar(equipo.id_equipo);

    return { ...equipo, ...responsable, ...observaciones };
  }

  async searchResponsable(nombreAdscripcion: string) {
    const adscripcion = await this.adscripcionRepo.findOneBy({
      adscripcion: nombreAdscripcion,
    });
    if (!adscripcion) {
      throw new Error(
        `No se encontró la adscripción con nombre ${nombreAdscripcion}`,
      );
    }
    let entrada = {
      tipoBusqueda: 'area',
      IdUnidadResponsable: adscripcion.id_adscripcion,
    };

    const url = process.env.API_FUNCIONARIOS;

    if (!url) throw new Error('Url no definida en las variables de entorno');

    const response = await axios.post(url, entrada);
    return response.data;
  }

  async create(equipo: CreateEquipoDto, id_usuario: number) {
    const equipoExistente = await this.equipoRepository.findOne({
      where: { inventario: equipo.inventario },
    });
    if (equipoExistente) {
      throw new BadRequestException('El equipo ya existe');
    }

    const datosBase = {
      inventario: equipo.inventario,
      serie: equipo.serie,
      lugar: equipo.lugar,
      fechaFactura: equipo.fechaFactura,
      antiguedad: equipo.antiguedad,
      modelo: equipo.modelo,
      estado: { id_estado: equipo.id_estado },
      adscripcion: { id_adscripcion: equipo.id_adscripcion },
      tipoUso: { id_uso: equipo.id_uso },
      marca: { id_marca: equipo.id_marca },
      tipoEquipo: { id_tipo_de_equipo: equipo.id_tipo_equipo },
      proyecto: { id_proyecto: equipo.id_proyecto },
      laboratorio: { id_laboratorio: equipo.id_laboratorio },
    };

    const nuevoEquipo = this.equipoRepository.create(
      equipo.isImpresora
        ? { ...datosBase, periferico: { id_periferico: equipo.id_periferico } }
        : {
          ...datosBase,
          sistemaOperativo: {
            id_sistema_operativo: equipo.id_sistema_operativo,
          },
          procesador: { id_procesador: equipo.id_procesador },
        },
    );

    let equip = await this.equipoRepository.save(nuevoEquipo);

    if (!equip) throw new Error('No se creó el equipo');

    // Crear movimiento asociado
    await this.movimiento.crear({
      idEquipo: equip.id_equipo,
      idUsuario: id_usuario,
    });

    return equip;
  }

  async findAllUsos() {
    return await this.usoRepo.find();
  }

  async findAllMarcas() {
    return await this.marcaRepo.find();
  }

  async findAllEstados() {
    return await this.estadoRepo.find();
  }

  async findAllProyectos() {
    return await this.proyectoRepo.find();
  }

  async findAllLaboratorios() {
    return await this.laboratorioRepo.find();
  }

  async findAllAdscripciones() {
    return await this.adscripcionRepo.find();
  }

  async findAllTiposEquipo() {
    return await this.tipoEquipoRepo.find();
  }

  async findAllSistemasOperativos() {
    return await this.sistemaOperativoRepo.find();
  }

  async findAllProcesadores() {
    return await this.procesadorRepo.find();
  }

  async findAllPerifericos() {
    return await this.perifericoRepo.find();
  }

  async findAllProcesadorTipoEquipos() {
    return await this.procesadorRepo.find();
  }

  // async createEcxel(res: Response) {
  //   const data = await this.generar_reporte();
  //   return this.exportarReporteAExcel(data, res);
  // }

  async createEcxel(res: Response) {
    return await this.exportarEquiposAExcel(res);
  }
  @Cron('0 0 0 * * *')
  async llenadoDeAntiguedad() {
    const result = await this.equipoRepository
      .createQueryBuilder()
      .update(Equipo)
      .set({
        antiguedad: () => `
      CASE
        WHEN fecha_factura IS NULL THEN 'SINFECHA'
        WHEN TIMESTAMPDIFF(YEAR, fecha_factura, CURDATE()) < 2 THEN 'MENORES DE 2'
        WHEN TIMESTAMPDIFF(YEAR, fecha_factura, CURDATE()) BETWEEN 2 AND 3 THEN 'ENTRE 2 Y 3'
        WHEN TIMESTAMPDIFF(YEAR, fecha_factura, CURDATE()) BETWEEN 4 AND 5 THEN 'ENTRE 4 Y 5'
        WHEN TIMESTAMPDIFF(YEAR, fecha_factura, CURDATE()) >= 6 THEN 'ENTRE 6 Y MAYORES'
      END
    `,
      })
      .execute();

    return result;
  }

  async exportarEquiposAExcel(res: Response) {
    const workbook = new ExcelJS.Workbook();
    const sheet = workbook.addWorksheet('Equipos');

    // =====================================
    // ✅ ENCABEZADOS
    // =====================================
    sheet.columns = [
      { header: 'Inventario', key: 'inventario', width: 70 },
      { header: 'Serie', key: 'serie', width: 70 },
      { header: 'Lugar', key: 'lugar', width: 70 },
      { header: 'Fecha Factura', key: 'fechaFactura', width: 70 },
      { header: 'Antigüedad', key: 'antiguedad', width: 70 },
      { header: 'Modelo', key: 'modelo', width: 70 },
      { header: 'Marca', key: 'marca', width: 70 },
      { header: 'Estado', key: 'estado', width: 70 },
      { header: 'Adscripción', key: 'adscripcion', width: 70 },
      { header: 'Tipo de Equipo', key: 'tipoEquipo', width: 70 },
      { header: 'Sistema Operativo', key: 'sistemaOperativo', width: 70 },
      { header: 'Procesador', key: 'procesador', width: 70 },
      { header: 'Tipo de Uso', key: 'tipoUso', width: 70 },
      { header: 'Periférico', key: 'periferico', width: 70 },
      { header: 'Proyecto', key: 'proyecto', width: 70 },
      { header: 'Laboratorio', key: 'laboratorio', width: 70 },
      { header: 'Observaciones', key: 'observaciones', width: 100 },
      { header: 'Fecha Movimiento', key: 'fechaMovimiento', width: 70 },
      { header: 'Usuario', key: 'user', width: 70 },
    ];

    // =====================================
    // ✅ OBTENER EQUIPOS
    // =====================================
    const equipos = await this.equipoRepository.find({
      relations: [
        'marca',
        'estado',
        'adscripcion',
        'tipoEquipo',
        'sistemaOperativo',
        'procesador',
        'tipoUso',
        'periferico',
        'proyecto',
        'laboratorio',
      ],
    });

    // =====================================
    // ✅ LLENAR FILAS
    // =====================================
    for (const equipo of equipos) {
      const movimiento = await this.movimiento.buscar(equipo.id_equipo);

      sheet.addRow({
        inventario: equipo.inventario,
        serie: equipo.serie,
        lugar: equipo.lugar,
        fechaFactura: equipo.fechaFactura,
        antiguedad: equipo.antiguedad,
        modelo: equipo.modelo,
        marca: equipo.marca?.marca ?? '',
        estado: equipo.estado?.estado ?? '',
        adscripcion: equipo.adscripcion?.adscripcion ?? '',
        tipoEquipo: equipo.tipoEquipo?.tipo_equipo ?? '',
        sistemaOperativo: equipo.sistemaOperativo?.sistema_operativo ?? '',
        procesador: equipo.procesador?.procesador ?? '',
        tipoUso: equipo.tipoUso?.tipo_uso ?? '',
        periferico: equipo.periferico?.periferico ?? '',
        proyecto: equipo.proyecto?.proyecto ?? '',
        laboratorio: equipo.laboratorio?.laboratorio ?? '',
        observaciones: movimiento?.observaciones ?? '',
        fechaMovimiento: movimiento?.fechaMovimiento ?? '',
        user: movimiento?.user?.nombre ?? '',
      });
    }

    // =====================================
    // ✅ ESTILO HEADER
    // =====================================
    sheet.getRow(1).font = { bold: true };

    // =====================================
    // ✅ RESPUESTA AL NAVEGADOR
    // =====================================
    res.setHeader(
      'Content-Type',
      'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
    );

    res.setHeader('Content-Disposition', 'attachment; filename=equipos.xlsx');

    await workbook.xlsx.write(res);
    res.end();
  }

  async exportarReporteAExcel(data: any, res: Response) {
    const workbook = new ExcelJS.Workbook();
    let count = 1;

    for (const key of Object.keys(data)) {
      let sheetName = key.substring(0, 30);

      if (workbook.getWorksheet(sheetName)) {
        sheetName = `${sheetName}_${count++}`;
      }

      const sheet = workbook.addWorksheet(sheetName);

      const rows = data[key];

      if (!Array.isArray(rows) || rows.length === 0) {
        sheet.addRow([`(sin datos)`]);
        continue;
      }

      const headers = Object.keys(rows[0]);

      sheet.addRow(headers);

      for (const row of rows) {
        sheet.addRow(headers.map((h) => row[h]));
      }

      sheet.getRow(1).font = { bold: true };
      sheet.getRow(1).alignment = { horizontal: 'center' };
      sheet.columns.forEach((col) => (col.width = 35));
    }

    res.setHeader(
      'Content-Type',
      'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
    );

    res.setHeader('Content-Disposition', 'attachment; filename=reporte.xlsx');

    await workbook.xlsx.write(res);
    res.status(200).end();
  }

  async createReport() {
    let obj = await this.generar_reporte();
    const objectParse = Array.isArray(obj)
      ? obj.map((item) => this.flattenObject(item))
      : [this.flattenObject(obj)];
    return objectParse;
  }

  async contar_tipoEquipos_tipoUso(estado: string[]): Promise<any> {
    try {
      const result = await this.equipoRepository
        .createQueryBuilder('e')
        .innerJoin('e.tipoEquipo', 't')
        .innerJoin('e.tipoUso', 'u')
        .innerJoin('e.sistemaOperativo', 'so')
        .innerJoin('e.estado', 'es')
        .select([
          't.tipo_equipo AS tipo_equipo',
          'u.tipo_uso AS tipo_uso',
          'so.sistema_operativo AS sistema_operativo',
        ])
        .addSelect('COUNT(*) AS total')
        .where('es.estado IN (:...estado)', { estado })
        .groupBy('t.id_tipo_de_equipo')
        .addGroupBy('u.id_uso')
        .addGroupBy('so.id_sistema_operativo')
        .getRawMany();

      return result;
    } catch (error) {
      console.error('Error al contar equipos por tipo, uso y SO:', error);
      throw error;
    }
  }

  async contar_tipoEquipos_sistemasOperativos(
    tipo_equipo: string[],
    estado: string[],
  ): Promise<any> {
    try {
      const count = await this.equipoRepository
        .createQueryBuilder('e')
        .innerJoin('e.tipoEquipo', 't')
        .innerJoin('e.sistemaOperativo', 's')
        .innerJoin('e.estado', 'es')
        .where('t.tipo_equipo IN (:...tipo_equipo)', { tipo_equipo })
        .andWhere('es.estado IN (:...estado)', { estado })
        .select(['s.sistema_operativo AS sistema_operativo'])
        .addSelect('COUNT(*) AS total')
        .groupBy('s.sistema_operativo')
        .getRawMany();

      return count;
    } catch (error) {
      console.error('Error al contar equipos por tipo y uso:', error);
      throw error;
    }
  }

  async contar_tipoEquipos_procesador(
    tipo_equipo: string[],
    estado: string[],
  ): Promise<any> {
    try {
      const count = await this.equipoRepository
        .createQueryBuilder('e')
        .innerJoin('e.procesador', 'p')
        .innerJoin('e.tipoEquipo', 't')
        .innerJoin('e.tipoUso', 'u')
        .innerJoin('e.estado', 'es')
        .where('t.tipo_equipo IN (:...tipo_equipo)', { tipo_equipo })
        .andWhere('es.estado IN (:...estado)', { estado })
        .select(['p.procesador AS procesador', 'u.tipo_uso AS uso'])
        .addSelect('COUNT(*) AS total')
        .groupBy('p.procesador')
        .addGroupBy('u.tipo_uso')
        .getRawMany();

      return count;
    } catch (error) {
      console.error('Error al contar equipos por tipo y uso:', error);
      throw error;
    }
  }

  async contar_tipoEquipos(
    tipo_equipo: string[],
    estado: string[],
  ): Promise<any> {
    try {
      const count = await this.equipoRepository
        .createQueryBuilder('e')
        .innerJoin('e.tipoEquipo', 't')
        .innerJoin('e.estado', 'es')
        .where('t.tipo_equipo IN (:...tipo_equipo)', { tipo_equipo })
        .andWhere('es.estado IN (:...estado)', { estado })
        .select('COUNT(*) AS total')
        .getRawOne();

      return count;
    } catch (error) {
      console.error('Error al contar equipos por tipo y uso:', error);
      throw error;
    }
  }

  async contar_tipoEquipos_antiguedad(estado: string[]): Promise<any> {
    const totalEscrtitorio = await this.contar_tipoEquipos(escritorio, estado);
    const totalPortatiles = await this.contar_tipoEquipos(portatil, estado);
    const totalAltoRendimiento = await this.contar_tipoEquipos(
      ['SERVIDOR'],
      estado,
    );
    try {
      const countEscritorios = await this.equipoRepository
        .createQueryBuilder('e')
        .innerJoin('e.tipoEquipo', 't')
        .innerJoin('e.estado', 'es')
        .where('t.tipo_equipo IN (:...tipo_equipo)', {
          tipo_equipo: escritorio,
        })
        .select(['e.antiguedad AS antiguedad'])
        .andWhere('es.estado IN (:...estado)', { estado })
        .addSelect('COUNT(*) AS total')
        .groupBy('e.antiguedad')
        .getRawMany();
      const countPortatiles = await this.equipoRepository
        .createQueryBuilder('e')
        .innerJoin('e.tipoEquipo', 't')
        .innerJoin('e.estado', 'es')
        .where('t.tipo_equipo IN (:...tipo_equipo)', {
          tipo_equipo: portatil,
        })
        .andWhere('es.estado IN (:...estado)', { estado })
        .select(['e.antiguedad AS antiguedad'])
        .addSelect('COUNT(*) AS total')
        .groupBy('e.antiguedad')
        .getRawMany();
      const countAltoRendimiento = await this.equipoRepository
        .createQueryBuilder('e')
        .innerJoin('e.tipoEquipo', 't')
        .innerJoin('e.estado', 'es')
        .where('t.tipo_equipo IN (:...tipo_equipo)', {
          tipo_equipo: ['SERVIDOR'],
        })
        .andWhere('es.estado IN (:...estado)', { estado })
        .select(['e.antiguedad AS antiguedad'])
        .addSelect('COUNT(*) AS total')
        .groupBy('e.antiguedad')
        .getRawMany();

      return {
        escritorios: countEscritorios.map((i) => ({
          antiguedad: i.antiguedad,
          total: i.total,
          porcentaje: ((i.total / totalEscrtitorio.total) * 100).toFixed(30),
        })),
        portatiles: countPortatiles.map((i) => ({
          antiguedad: i.antiguedad,
          total: i.total,
          porcentaje: ((i.total / totalPortatiles.total) * 100).toFixed(30),
        })),
        altoRendimiento: countAltoRendimiento.map((i) => ({
          antiguedad: i.antiguedad,
          total: i.total,
          porcentaje: ((i.total / totalAltoRendimiento.total) * 100).toFixed(
            30,
          ),
        })),
      };
    } catch (error) {
      console.error('Error al contar equipos por tipo y uso:', error);
      throw error;
    }
  }

  async garantia(tipo_equipo: string[], estado: string[]): Promise<any> {
    try {
      const count = await this.equipoRepository
        .createQueryBuilder('e')
        .innerJoin('e.tipoEquipo', 't')
        .innerJoin('e.estado', 'es')
        .where('e.fechaFactura >= DATE_SUB(CURDATE(), INTERVAL 3 YEAR)')
        .andWhere('t.tipo_equipo IN (:...tipo_equipo)', { tipo_equipo })
        .andWhere('es.estado IN (:...estado)', { estado })
        .getCount();

      return count;
    } catch (error) {
      console.error('Error al contar equipos por tipo y uso:', error);
      throw error;
    }
  }

  async equipos_impesion(
    perifericos: string[],
    estado: string[],
  ): Promise<any> {
    try {
      const count = await this.equipoRepository
        .createQueryBuilder('e')
        .innerJoin('e.periferico', 'p')
        .innerJoin('e.estado', 'es')
        .select(['p.periferico AS periferico'])
        .where('p.periferico IN (:...perifericos)', { perifericos })
        .andWhere('es.estado IN (:...estado)', { estado })
        .getCount();

      return count;
    } catch (error) {
      console.error('Error al contar equipos por tipo y uso:', error);
      throw error;
    }
  }

  async equipos_impesion_group(
    perifericos: string[],
    estado: string[],
  ): Promise<any> {
    try {
      const count = await this.equipoRepository
        .createQueryBuilder('e')
        .innerJoin('e.periferico', 'p')
        .innerJoin('e.estado', 'es')
        .where('p.periferico IN (:...perifericos)', { perifericos })
        .andWhere('es.estado IN (:...estado)', { estado })
        .select('p.periferico AS periferico')
        .addSelect('COUNT(*) AS total')
        .groupBy('p.periferico')
        .getRawMany();

      return count;
    } catch (error) {
      console.error('Error al contar equipos por tipo y uso:', error);
      throw error;
    }
  }

  async contar_periferico_tipoUso(
    perifericos: string[],
    estado: string[],
  ): Promise<any> {
    try {
      const count = await this.equipoRepository
        .createQueryBuilder('e')
        .innerJoin('e.tipoUso', 't')
        .innerJoin('e.periferico', 'p')
        .innerJoin('e.estado', 'es')
        .where('p.periferico IN (:...perifericos)', { perifericos })
        .andWhere('es.estado IN (:...estado)', { estado })
        .select(['t.tipo_uso AS uso'])
        .addSelect('COUNT(*) AS total')
        .groupBy('t.tipo_uso')
        .getRawMany();

      return count;
    } catch (error) {
      console.error('Error al contar equipos por tipo y uso:', error);
      throw error;
    }
  }

  async contar_perifericos_antiguedad(
    impresion: string[],
    digitalizacion: string[],
    estado: string[],
  ): Promise<{
    impresion: { antiguedad: any; total: number; porcentaje: string }[];
    digitalizacion: { antiguedad: any; total: number; porcentaje: string }[];
  }> {
    try {
      // Helper para obtener total por tipos de equipo (count simple, sin group by)
      const getTotalPorTipos = async (tipos: string[]): Promise<any> => {
        if (!tipos || tipos.length === 0) return 0;
        return this.equipoRepository
          .createQueryBuilder('e')
          .innerJoin('e.periferico', 'p')
          .innerJoin('e.estado', 'es')
          .where('p.periferico IN (:...tipos)', { tipos })
          .andWhere('es.estado IN (:...estado)', { estado })
          .select('COUNT(*) AS total')
          .getRawOne();
      };

      // Ejecutar ambos totales en paralelo

      const [totalI, totalD] = await Promise.all([
        getTotalPorTipos(impresion),
        getTotalPorTipos(digitalizacion),
      ]);
      const totalImpresion = totalI.total;

      const totalDigitalizacion = totalD.total;

      // Obtener conteos por antiguedad para impresion
      const countImpresionPromise = this.equipoRepository
        .createQueryBuilder('e')
        .innerJoin('e.periferico', 'p')
        .innerJoin('e.tipoEquipo', 't')
        .innerJoin('e.estado', 'es')
        .where('p.periferico IN (:...periferico)', { periferico: impresion })
        .andWhere('es.estado IN (:...estado)', { estado })
        .select(['e.antiguedad AS antiguedad'])
        .addSelect('COUNT(e.id_equipo) AS total')
        .groupBy('e.antiguedad')
        .getRawMany();

      // Obtener conteos por antiguedad para digitalizacion
      const countDigitalizacionPromise = this.equipoRepository
        .createQueryBuilder('e')
        .innerJoin('e.periferico', 'p')
        .innerJoin('e.tipoEquipo', 't')
        .innerJoin('e.estado', 'es')
        .where('p.periferico IN (:...periferico)', {
          periferico: digitalizacion,
        })
        .andWhere('es.estado IN (:...estado)', { estado })
        .select(['e.antiguedad AS antiguedad'])
        .addSelect('COUNT(e.id_equipo) AS total')
        .groupBy('e.antiguedad')
        .getRawMany();

      // Ejecutar ambas consultas en paralelo
      const [countImpresion, countDigitalizacion] = await Promise.all([
        countImpresionPromise,
        countDigitalizacionPromise,
      ]);

      // Tipo para salida
      interface Porcentaje {
        antiguedad: any;
        total: number;
        porcentaje: string;
      }

      // Mapear resultados calculando porcentajes (evitar async dentro de map/forEach)
      const porcentajesImpresion: Porcentaje[] = countImpresion.map((item) => {
        const total = Number(item.total) || 0;
        const porcentaje =
          totalImpresion > 0
            ? ((total / totalImpresion) * 100).toFixed(30)
            : '0.00';
        return {
          antiguedad: item.antiguedad,
          total,
          porcentaje,
        };
      });

      const porcentajesDigitalizacion: Porcentaje[] = countDigitalizacion.map(
        (item) => {
          const total = Number(item.total) || 0;
          const porcentaje =
            totalDigitalizacion > 0
              ? ((total / totalDigitalizacion) * 100).toFixed(30)
              : '0.00';
          return {
            antiguedad: item.antiguedad,
            total,
            porcentaje,
          };
        },
      );

      return {
        impresion: porcentajesImpresion,
        digitalizacion: porcentajesDigitalizacion,
      };
    } catch (error) {
      console.error('Error en contar_perifericos_antiguedad:', error);
      throw error;
    }
  }

  async proyecto_equipos(estado: string[]) {
    try {
      const count = await this.equipoRepository
        .createQueryBuilder('e')
        .innerJoin('e.proyecto', 'p')
        .innerJoin('e.estado', 'es')
        .select(['p.proyecto AS proyecto'])
        .andWhere('es.estado IN (:...estado)', { estado })
        .addSelect('COUNT(*) AS total')
        .groupBy('p.proyecto')
        .getRawMany();

      return count;
    } catch (error) {
      console.error('Error al contar equipos proyectos:', error);
      throw error;
    }
  }

  async laboratorio_equipos(estado: string[]) {
    try {
      const count = await this.equipoRepository
        .createQueryBuilder('e')
        .innerJoin('e.laboratorio', 'l')
        .innerJoin('e.estado', 'es')
        .select(['l.laboratorio AS laboratorio'])
        .andWhere('es.estado IN (:...estado)', { estado })
        .addSelect('COUNT(*) AS total')
        .groupBy('l.laboratorio')
        .getRawMany();

      return count;
    } catch (error) {
      console.error('Error al contar equipos proyectos:', error);
      throw error;
    }
  }

  async generar_reporte(estado: string[] = estado_excluidos) {
    const [
      contar_equipos_de_tipo_uso,
      sisitema_operativo_pc,
      sisitema_operativo_mac,
      sisitema_operativo_portatiles_pc,
      sisitema_operativo_portatiles_mac,
      sisitema_operativo_servidores,
      contar_pc_procesador,
      contar_mac_procesador,
      contar_portatil_procesador,
      contar_portatil_mac_procesador,
      contar_servidor_procesador,
      porcentaje_antiguedad,
      garantia_escritorio,
      garantia_portatiles,
      garantia_servidor,
      contar_impresoras,
      contar_impresoras_tipo_uso,
      contar_digitalizacion,
      contar_contar_digitalizacion_u,
      contar_perifericos_antiguedad,
      cotar_laboratorios,
      contar_proyectos,
    ] = await Promise.all([
      this.contar_tipoEquipos_tipoUso(estado),
      this.contar_tipoEquipos_sistemasOperativos(equpos_pc, estado),
      this.contar_tipoEquipos_sistemasOperativos([estritorio_mac], estado),
      this.contar_tipoEquipos_sistemasOperativos(portatiles, estado),
      this.contar_tipoEquipos_sistemasOperativos([portatiles_mac], estado),
      this.contar_tipoEquipos_sistemasOperativos([servidor], estado),
      this.contar_tipoEquipos_procesador(equpos_pc, estado),
      this.contar_tipoEquipos_procesador([estritorio_mac], estado),
      this.contar_tipoEquipos_procesador(portatiles, estado),
      this.contar_tipoEquipos_procesador([portatiles_mac], estado),
      this.contar_tipoEquipos_procesador([servidor], estado),
      this.contar_tipoEquipos_antiguedad(estado),
      this.garantia(escritorio, estado),
      this.garantia(portatil, estado),
      this.garantia([servidor], estado),
      this.equipos_impesion_group(impresoras, estado),
      this.contar_periferico_tipoUso(impresoras, estado),
      this.equipos_impesion_group(dijitales, estado),
      this.contar_periferico_tipoUso(dijitales, estado),
      this.contar_perifericos_antiguedad(impresoras, dijitales, estado),
      this.proyecto_equipos(estado),
      this.laboratorio_equipos(estado),
    ]);

    return {
      contar_equipos_de_tipo_uso,
      sisitema_operativo_pc,
      sisitema_operativo_mac,
      sisitema_operativo_portatiles_pc,
      sisitema_operativo_portatiles_mac,
      sisitema_operativo_servidores,
      contar_pc_procesador,
      contar_mac_procesador,
      contar_portatil_procesador,
      contar_portatil_mac_procesador,
      contar_servidor_procesador,
      porcentaje_antiguedad,
      garantia_escritorio,
      garantia_portatiles,
      garantia_servidor,
      contar_impresoras,
      contar_impresoras_tipo_uso,
      contar_digitalizacion,
      contar_contar_digitalizacion_u,
      contar_perifericos_antiguedad,
      cotar_laboratorios,
      contar_proyectos,
    };
  }

  async exportToClient(data: any, res: Response) {
    // Aseguramos que los datos sean un arreglo de objetos planos
    const rows = Array.isArray(data)
      ? data.map((item) => this.flattenObject(item))
      : [this.flattenObject(data)];

    // Crear workbook y hoja
    const workbook = new ExcelJS.Workbook();
    const worksheet = workbook.addWorksheet('Datos');

    // Definir encabezados dinámicamente
    const headers = Object.keys(rows[0] || {});
    worksheet.columns = headers.map((h) => ({
      header: h,
      key: h,
      width: 25,
    }));

    // Agregar filas
    worksheet.addRows(rows);

    // Estilizar encabezados
    const headerRow = worksheet.getRow(1);
    headerRow.font = { bold: true };
    headerRow.alignment = { vertical: 'middle', horizontal: 'center' };
    headerRow.eachCell((cell) => {
      cell.border = {
        top: { style: 'thin' },
        left: { style: 'thin' },
        bottom: { style: 'thin' },
        right: { style: 'thin' },
      };
    });

    // Configurar cabeceras HTTP
    res.setHeader(
      'Content-Type',
      'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
    );
    res.setHeader('Content-Disposition', 'attachment; filename="reporte.xlsx"');

    // Enviar directamente al cliente
    await workbook.xlsx.write(res);
    res.end();
  }

  private flattenObject(obj: any, parentKey = '', res: any = {}): any {
    for (const key of Object.keys(obj || {})) {
      const propName = parentKey ? `${parentKey}.${key}` : key;
      const value = obj[key];

      if (value && typeof value === 'object' && !Array.isArray(value)) {
        this.flattenObject(value, propName, res);
      } else if (Array.isArray(value)) {
        res[propName] = JSON.stringify(value);
      } else {
        res[propName] = value;
      }
    }
    return res;
  }

  async ranking(year: number) {
    const equipos = await this.equipoRepository.find({
      relations: ['mov', 'mov.user'],
    });

    const conteo = new Map<
      number,
      { id_usuario: number; nombre: string; total: number }
    >();

    for (const equipo of equipos) {
      if (!equipo.mov || equipo.mov.length === 0) continue;

      const equipoyear = equipo.mov.filter((mov) => {
        const fecha = new Date(mov.fechaMovimiento);
        return fecha.getFullYear() === year;
      });

      if (equipoyear.length === 0) continue;

      const ultimoMov = equipoyear.sort(
        (a, b) =>
          new Date(b.fechaMovimiento).getTime() -
          new Date(a.fechaMovimiento).getTime(),
      )[0];

      const user = ultimoMov.user;
      if (!user) continue;

      if (!conteo.has(user.id_usuario)) {
        conteo.set(user.id_usuario, {
          id_usuario: user.id_usuario,
          nombre: user.nombre,
          total: 0,
        });
      }

      conteo.get(user.id_usuario)!.total++;
    }

    return Array.from(conteo.values()).sort((a, b) => b.total - a.total);
  }

  async grafica(
    tipo: 'antiguedad' | 'uso' | 'so' | 'procesador',
    filtros?: FiltrosGraficaDto
  ) {

    const qb = this.equipoRepository
      .createQueryBuilder('equipo')
      .leftJoin('equipo.adscripcion', 'adscripcion')
      .leftJoin('equipo.procesador', 'procesador')
      .leftJoin('equipo.sistemaOperativo', 'sistemaOperativo')
      .leftJoin('equipo.tipoUso', 'tipoUso')

    // filtros

    if (filtros?.adscripciones?.length) {
      qb.andWhere('adscripcion.id_adscripcion IN (:...adscripciones)', {
        adscripciones: filtros.adscripciones
      })
        .addSelect('adscripcion.adscripcion', 'adscripcion')
        .groupBy('equipo.adscripcion')

    }

    if (filtros?.procesadores?.length) {
      qb.andWhere('procesador.id_procesador IN (:...procesadores)', {
        procesadores: filtros.procesadores
      })
    }

    if (filtros?.sistemas?.length) {
      qb.andWhere('sistemaOperativo.id_sistema_operativo IN (:...sistemas)', {
        sistemas: filtros.sistemas
      })
    }

    if (filtros?.usos?.length) {
      qb.andWhere('tipoUso.id_uso IN (:...usos)', {
        usos: filtros.usos
      })
    }

    if (filtros?.antiguedad?.length) {
      qb.andWhere('equipo.antiguedad IN (:...antiguedad)', {
        antiguedad: filtros.antiguedad
      })
    }

    // selector dinámico

    switch (tipo) {

      case 'antiguedad':

        qb.select('equipo.antiguedad', 'antiguedad')
          .addSelect('COUNT(*)', 'total')

        if (filtros?.adscripciones?.length) {

          qb.addSelect('adscripcion.adscripcion', 'adscripcion')

          qb.groupBy('equipo.antiguedad')
            .addGroupBy('adscripcion.adscripcion')

        } else {

          qb.groupBy('equipo.antiguedad')

        }

        qb.orderBy(`
          CASE
            WHEN equipo.antiguedad = 'Menor a 2 años' THEN 1
            WHEN equipo.antiguedad = 'Entre 2 y 3 años' THEN 2
            WHEN equipo.antiguedad = 'Entre 4 y 5 años' THEN 3
            WHEN equipo.antiguedad = 'Mayor a 6 años' THEN 4
          END
        `)

        break

      case 'uso':

        qb.select('tipoUso.tipo_uso', 'uso')
          .addSelect('COUNT(*)', 'total')

        if (filtros?.adscripciones?.length) {

          qb.addSelect('adscripcion.adscripcion', 'adscripcion')

          qb.groupBy('tipoUso.tipo_uso')
            .addGroupBy('adscripcion.adscripcion')

        } else {

          qb.groupBy('tipoUso.tipo_uso')

        }

        qb.orderBy(`equipo.adscripcion`, "ASC")

        break

      case 'so':

        qb.select('sistemaOperativo.sistema_operativo', 'so')
          .addSelect('COUNT(*)', 'total')

        if (filtros?.adscripciones?.length) {

          qb.addSelect('adscripcion.adscripcion', 'adscripcion')

          qb.groupBy('sistemaOperativo.sistema_operativo')
            .addGroupBy('adscripcion.adscripcion')

        } else {

          qb.groupBy('sistemaOperativo.sistema_operativo')

        }
        qb.orderBy(`equipo.adscripcion`, "ASC")

        break

      case 'procesador':

        qb.select('procesador.procesador', 'procesador')
          .addSelect('COUNT(*)', 'total')

        if (filtros?.adscripciones?.length) {

          qb.addSelect('adscripcion.adscripcion', 'adscripcion')

          qb.groupBy('procesador.procesador')
            .addGroupBy('adscripcion.adscripcion')

        } else {

          qb.groupBy('procesador.procesador')

        }
        qb.orderBy(`equipo.adscripcion`, "ASC")

        break

    }
    return qb.getRawMany()

  }

  async tabla(filtros?: FiltrosTablaDto) {
    const qb = this.equipoRepository
      .createQueryBuilder('equipo')
      .select([])

    qb.leftJoin('equipo.adscripcion', 'adscripcion')
    qb.leftJoin('equipo.procesador', 'procesador')
    qb.leftJoin('equipo.sistemaOperativo', 'sistemaOperativo')
    qb.leftJoin('equipo.tipoUso', 'tipoUso')

    qb.addSelect('equipo.inventario', 'inventario')
    qb.addSelect('adscripcion.adscripcion', 'adscripcion')
    qb.addSelect('equipo.lugar', 'ubicacion')

    const tieneFiltroReal = (arr?: string[]) => arr?.length && !arr.includes("0")

    console.log(filtros)

    // ADSCRIPCIONES
    if (filtros?.adscripciones?.length) {

      // siempre selecciona
      qb.addSelect('adscripcion.adscripcion', 'adscripcion')

      // solo filtra si no es "Todos"
      if (tieneFiltroReal(filtros.adscripciones)) {
        qb.andWhere('adscripcion.id_adscripcion IN (:...adscripciones)', {
          adscripciones: filtros.adscripciones
        })
      }
    }

    // PROCESADOR
    if (filtros?.procesadores?.length) {

      qb.addSelect('procesador.procesador', 'procesador')

      if (tieneFiltroReal(filtros.procesadores)) {
        qb.andWhere('procesador.id_procesador IN (:...procesadores)', {
          procesadores: filtros.procesadores
        })
      }
    }

    // SISTEMA OPERATIVO
    if (filtros?.sistemas?.length) {

      qb.addSelect('sistemaOperativo.sistema_operativo', 'so')

      if (tieneFiltroReal(filtros.sistemas)) {
        qb.andWhere('sistemaOperativo.id_sistema_operativo IN (:...sistemas)', {
          sistemas: filtros.sistemas
        })
      }
    }

    // USO
    if (filtros?.usos?.length) {

      qb.addSelect('tipoUso.tipo_uso', 'uso')

      if (tieneFiltroReal(filtros.usos)) {
        qb.andWhere('tipoUso.id_uso IN (:...usos)', {
          usos: filtros.usos
        })
      }
    }

    // ANTIGÜEDAD
    if (filtros?.antiguedad?.length) {

      qb.addSelect('equipo.antiguedad', 'antiguedad')

      if (tieneFiltroReal(filtros.antiguedad)) {
        qb.andWhere('equipo.antiguedad IN (:...antiguedad)', {
          antiguedad: filtros.antiguedad
        })
      }
    }

    return qb
      .orderBy(`equipo.adscripcion`, "DESC")
      .getRawMany()
  }

  async tablaCount(filtros?: FiltrosTablaDto) {
    const qb = this.equipoRepository
      .createQueryBuilder('equipo')
      .select([])

    qb.leftJoin('equipo.adscripcion', 'adscripcion')
    qb.leftJoin('equipo.procesador', 'procesador')
    qb.leftJoin('equipo.sistemaOperativo', 'sistemaOperativo')
    qb.leftJoin('equipo.tipoUso', 'tipoUso')

    qb.addSelect('adscripcion.adscripcion', 'adscripcion')
    if (filtros?.adscripciones?.length) {
      qb.andWhere('adscripcion.id_adscripcion IN (:...adscripciones)', {
        adscripciones: filtros.adscripciones
      })
    }

    if (filtros?.procesadores?.length) {
      qb.andWhere('procesador.id_procesador IN (:...procesadores)', {
        procesadores: filtros.procesadores
      })
    }

    if (filtros?.sistemas?.length) {
      qb.andWhere('sistemaOperativo.id_sistema_operativo IN (:...sistemas)', {
        sistemas: filtros.sistemas
      })
    }

    if (filtros?.usos?.length) {
      qb.andWhere('tipoUso.id_uso IN (:...usos)', {
        usos: filtros.usos
      })
    }

    if (filtros?.antiguedad?.length) {
      qb.andWhere('equipo.antiguedad IN (:...antiguedad)', {
        antiguedad: filtros.antiguedad
      })
    }

    return qb
      .addSelect('COUNT(*)', 'total')
      .addGroupBy('adscripcion.adscripcion')
      .orderBy(`equipo.adscripcion`, "DESC")
      .getRawMany()
  }

  async tablaExcel(filtros?: FiltrosTablaDto) {
    const qb = this.equipoRepository
      .createQueryBuilder('equipo')
      .select([]);

    qb.leftJoin('equipo.adscripcion', 'adscripcion');
    qb.leftJoin('equipo.procesador', 'procesador');
    qb.leftJoin('equipo.sistemaOperativo', 'sistemaOperativo');
    qb.leftJoin('equipo.tipoUso', 'tipoUso');

    qb.addSelect('equipo.inventario', 'inventario');
    qb.addSelect('adscripcion.adscripcion', 'adscripcion');
    qb.addSelect('equipo.lugar', 'ubicacion');

    const tieneFiltroReal = (arr?: string[]) => arr?.length && !arr.includes("0");

    // ADSCRIPCIONES
    if (filtros?.adscripciones?.length) {

      qb.addSelect('adscripcion.adscripcion', 'adscripcion');

      if (tieneFiltroReal(filtros.adscripciones)) {
        qb.andWhere('adscripcion.id_adscripcion IN (:...adscripciones)', {
          adscripciones: filtros.adscripciones
        });
      }
    }

    // PROCESADOR
    if (filtros?.procesadores?.length) {

      qb.addSelect('procesador.procesador', 'procesador');

      if (tieneFiltroReal(filtros.procesadores)) {
        qb.andWhere('procesador.id_procesador IN (:...procesadores)', {
          procesadores: filtros.procesadores
        });
      }
    }

    // SISTEMA OPERATIVO
    if (filtros?.sistemas?.length) {

      qb.addSelect('sistemaOperativo.sistema_operativo', 'so');

      if (tieneFiltroReal(filtros.sistemas)) {
        qb.andWhere('sistemaOperativo.id_sistema_operativo IN (:...sistemas)', {
          sistemas: filtros.sistemas
        });
      }
    }

    // USO
    if (filtros?.usos?.length) {

      qb.addSelect('tipoUso.tipo_uso', 'uso');

      if (tieneFiltroReal(filtros.usos)) {
        qb.andWhere('tipoUso.id_uso IN (:...usos)', {
          usos: filtros.usos
        });
      }
    }

    // ANTIGÜEDAD
    if (filtros?.antiguedad?.length) {

      qb.addSelect('equipo.antiguedad', 'antiguedad');

      if (tieneFiltroReal(filtros.antiguedad)) {
        qb.andWhere('equipo.antiguedad IN (:...antiguedad)', {
          antiguedad: filtros.antiguedad
        });
      }
    }

    const data = await qb
      .orderBy(`equipo.adscripcion`, "DESC")
      .getRawMany();

    const workbook = new ExcelJS.Workbook();
    const sheet = workbook.addWorksheet('Equipos');

    const columns = [
      { header: 'Inventario', key: 'inventario', width: 20 },
      { header: 'Ubicación', key: 'ubicacion', width: 40 },
      { header: 'Adscripción', key: 'adscripcion', width: 40 },
    ];

    if (data[0]?.procesador !== undefined) {
      columns.push({ header: 'Procesador', key: 'procesador', width: 25 });
    }

    if (data[0]?.so !== undefined) {
      columns.push({ header: 'Sistema Operativo', key: 'so', width: 25 });
    }

    if (data[0]?.uso !== undefined) {
      columns.push({ header: 'Uso', key: 'uso', width: 20 });
    }

    if (data[0]?.antiguedad !== undefined) {
      columns.push({ header: 'Antigüedad', key: 'antiguedad', width: 15 });
    }

    sheet.columns = columns;

    data.forEach(row => {
      sheet.addRow(row);
    });

    sheet.getRow(1).font = { bold: true };

    return await workbook.xlsx.writeBuffer();
  }

  async tablaCountExcel(filtros?: FiltrosGraficaDto) {
    const qb = this.equipoRepository
      .createQueryBuilder('equipo')
      .select([]);

    qb.leftJoin('equipo.adscripcion', 'adscripcion');
    qb.leftJoin('equipo.procesador', 'procesador');
    qb.leftJoin('equipo.sistemaOperativo', 'sistemaOperativo');
    qb.leftJoin('equipo.tipoUso', 'tipoUso');
    qb.addSelect('adscripcion.adscripcion', 'adscripcion');

    if (filtros?.adscripciones?.length) {
      qb.andWhere('adscripcion.id_adscripcion IN (:...adscripciones)', {
        adscripciones: filtros.adscripciones
      });
    }

    if (filtros?.procesadores?.length) {
      qb.andWhere('procesador.id_procesador IN (:...procesadores)', {
        procesadores: filtros.procesadores
      });
    }

    if (filtros?.sistemas?.length) {
      qb.andWhere('sistemaOperativo.id_sistema_operativo IN (:...sistemas)', {
        sistemas: filtros.sistemas
      });
    }

    if (filtros?.usos?.length) {
      qb.andWhere('tipoUso.id_uso IN (:...usos)', {
        usos: filtros.usos
      });
    }

    if (filtros?.antiguedad?.length) {
      qb.andWhere('equipo.antiguedad IN (:...antiguedad)', {
        antiguedad: filtros.antiguedad
      });
    }

    const data = await qb
      .addSelect('COUNT(*)', 'total')
      .addGroupBy('adscripcion.adscripcion')
      .orderBy(`equipo.adscripcion`, "DESC")
      .getRawMany();

    const workbook = new ExcelJS.Workbook();
    const sheet = workbook.addWorksheet('Resumen');

    sheet.columns = [
      { header: 'Adscripción', key: 'adscripcion', width: 30 },
      { header: 'Total', key: 'total', width: 15 },
    ];

    data.forEach(row => {
      sheet.addRow(row);
    });

    sheet.getRow(1).font = { bold: true };

    return await workbook.xlsx.writeBuffer();
  }
}

let estado_excluidos = ['DE BAJA'];
let estado_inclyuidos = ['EN DESUSO', 'EN USO'];

let escritorio = ['ESCRITORIO PC', 'ESCRITORIO MAC OS ', 'ESCRITORIO LINUX'];
let portatil = [
  'PORTÁTILES WINDOWS',
  'PORTÁTILES CHROMEBOOK',
  'PORTÁTILES MAC OS',
];

let equipos_mac = ['ESCRITORIO MAC OS ', 'PORTÁTILES MAC OS'];

let equpos_pc = ['ESCRITORIO PC', 'ESCRITORIO LINUX'];
let estritorio_mac = 'ESCRITORIO MAC OS ';
let portatiles_mac = 'PORTÁTILES MAC OS';

let portatiles = ['PORTÁTILES WINDOWS', 'PORTÁTILES CHROMEBOOK'];

let tabletas = ['TABLETA ANDROID', 'TABLETA iPAD OS'];

let impresoras = [
  'INYECCIÓN TINTA',
  'LÁSER DE ALTO VOLUMEN B/N',
  'LÁSER DE ALTO VOLUMEN COLOR',
  'LÁSER PEQUEÑA B/N',
  'LÁSER PEQUEÑA COLOR',
  'MATRIZ DE PUNTOS',
  'MULTIFUNCIONALES',
  'IMPRESORA TÉRMICA',
  'IMPRESORA CREDENCIALES',
  'PLOTTER',
];

let dijitales = [
  '3D',
  'DIGITALIZADOR DE CAMA PLANA PARA OFICINA',
  'DIGITALIZADOR DE GRAN VOLUMEN',
  'DIGITALIZADOR CON ALIMENTADOR DE HOJAS PARA OFICINA',
];

let servidor = 'SERVIDOR';
