import {
  BadRequestException,
  Injectable,
  NotFoundException,
} from "@nestjs/common";
import * as ExcelJS from "exceljs";
import { Repository } from "typeorm";
import { TenantRepositoryAccessor } from "../tenancy/tenant-repository.accessor";
import { CreateCatalogoVehicularDto } from "./dto/create-catalogo-vehicular.dto";
import { CosaTrae } from "./entities/cosa-trae.entity";
import {
  CatalogoVehicular,
  CatalogoVehicularTipo,
} from "./entities/catalogo-vehicular.entity";
import { TipoDano } from "./entities/tipo-dano.entity";
import { TipoVehiculo } from "./entities/tipo-vehiculo.entity";

@Injectable()
export class CatalogosService {
  constructor(private readonly tenantAccessor: TenantRepositoryAccessor) {}

  private get tipoVehiculoRepo(): Repository<TipoVehiculo> {
    return this.tenantAccessor.repositoryFor(TipoVehiculo);
  }

  private get cosaTraeRepo(): Repository<CosaTrae> {
    return this.tenantAccessor.repositoryFor(CosaTrae);
  }

  private get tipoDanoRepo(): Repository<TipoDano> {
    return this.tenantAccessor.repositoryFor(TipoDano);
  }

  private get catalogoVehicularRepo(): Repository<CatalogoVehicular> {
    return this.tenantAccessor.repositoryFor(CatalogoVehicular);
  }

  private normalize(value?: string | null): string {
    return value?.trim() || "";
  }

  private normalizeKey(value?: string | null): string {
    return this.normalize(value).toLowerCase();
  }

  private normalizeHeader(value?: string | null): string {
    return this.normalizeKey(value)
      .normalize("NFD")
      .replace(/[\u0300-\u036f]/g, "")
      .replace(/[^a-z0-9]/g, "");
  }

  private ensureTipoVehiculoId(value?: number | null): number {
    const n = Number(value);
    if (!Number.isInteger(n) || n <= 0) {
      throw new BadRequestException("tipoVehiculoId es obligatorio");
    }
    return n;
  }

  private async ensureTipoVehiculoActivo(
    tipoVehiculoId: number,
  ): Promise<void> {
    const tipo = await this.tipoVehiculoRepo.findOne({
      where: { id: tipoVehiculoId, activo: true },
    });
    if (!tipo) {
      throw new BadRequestException(
        "El tipo de vehículo seleccionado no existe o está inactivo",
      );
    }
  }

  private async findActiveVehicular(
    tipo: CatalogoVehicularTipo,
    nombre: string,
    ctx?: {
      marca?: string;
      modelo?: string;
      motor?: string;
      tipoVehiculoId?: number;
    },
    excludeId?: number,
  ): Promise<CatalogoVehicular | null> {
    const qb = this.catalogoVehicularRepo
      .createQueryBuilder("cat")
      .where("cat.tipo = :tipo", { tipo })
      .andWhere("cat.activo = true")
      .andWhere("LOWER(cat.nombre) = :nombre", {
        nombre: this.normalizeKey(nombre),
      });

    if (ctx?.marca) {
      qb.andWhere("LOWER(cat.marca) = :marca", {
        marca: this.normalizeKey(ctx.marca),
      });
    }

    if (ctx?.modelo) {
      qb.andWhere("LOWER(cat.modelo) = :modelo", {
        modelo: this.normalizeKey(ctx.modelo),
      });
    }

    if (ctx?.motor) {
      qb.andWhere("LOWER(cat.motor) = :motor", {
        motor: this.normalizeKey(ctx.motor),
      });
    }

    if (ctx?.tipoVehiculoId) {
      qb.andWhere("cat.tipoVehiculoId = :tipoVehiculoId", {
        tipoVehiculoId: ctx.tipoVehiculoId,
      });
    }

    if (excludeId) {
      qb.andWhere("cat.id <> :excludeId", { excludeId });
    }

    return qb.orderBy("cat.id", "DESC").getOne();
  }

  private async findVehicularAny(
    tipo: CatalogoVehicularTipo,
    nombre: string,
    ctx?: {
      marca?: string;
      modelo?: string;
      motor?: string;
      tipoVehiculoId?: number;
    },
  ): Promise<CatalogoVehicular | null> {
    const qb = this.catalogoVehicularRepo
      .createQueryBuilder("cat")
      .where("cat.tipo = :tipo", { tipo })
      .andWhere("LOWER(cat.nombre) = :nombre", {
        nombre: this.normalizeKey(nombre),
      });

    if (ctx?.marca) {
      qb.andWhere("LOWER(cat.marca) = :marca", {
        marca: this.normalizeKey(ctx.marca),
      });
    }

    if (ctx?.modelo) {
      qb.andWhere("LOWER(cat.modelo) = :modelo", {
        modelo: this.normalizeKey(ctx.modelo),
      });
    }

    if (ctx?.motor) {
      qb.andWhere("LOWER(cat.motor) = :motor", {
        motor: this.normalizeKey(ctx.motor),
      });
    }

    if (ctx?.tipoVehiculoId) {
      qb.andWhere("cat.tipoVehiculoId = :tipoVehiculoId", {
        tipoVehiculoId: ctx.tipoVehiculoId,
      });
    }

    return qb
      .orderBy("cat.activo", "DESC")
      .addOrderBy("cat.id", "DESC")
      .getOne();
  }

  private async ensureTipoVehiculoByName(
    nombre: string,
    userId?: number,
  ): Promise<{ row: TipoVehiculo; created: boolean; reactivated: boolean }> {
    const normalized = this.normalize(nombre);
    if (!normalized) {
      throw new BadRequestException("El tipo de vehículo es obligatorio");
    }

    let row = await this.tipoVehiculoRepo
      .createQueryBuilder("tipo")
      .where("LOWER(tipo.nombre) = :nombre", {
        nombre: this.normalizeKey(normalized),
      })
      .orderBy("tipo.activo", "DESC")
      .addOrderBy("tipo.id", "DESC")
      .getOne();

    if (!row) {
      row = this.tipoVehiculoRepo.create({
        nombre: normalized,
        plantillaDanos: "carro",
        activo: true,
        createdBy: userId ?? null,
        updatedBy: userId ?? null,
      });
      return {
        row: await this.tipoVehiculoRepo.save(row),
        created: true,
        reactivated: false,
      };
    }

    if (!row.activo) {
      row.activo = true;
      row.updatedBy = userId ?? null;
      row = await this.tipoVehiculoRepo.save(row);
      return { row, created: false, reactivated: true };
    }

    return { row, created: false, reactivated: false };
  }

  private async ensureVehicularByName(
    tipo: CatalogoVehicularTipo,
    nombre: string,
    payload: {
      marca?: string;
      modelo?: string;
      motor?: string;
      tipoVehiculoId: number;
    },
  ): Promise<{
    row: CatalogoVehicular;
    created: boolean;
    reactivated: boolean;
  }> {
    const normalized = this.normalize(nombre);
    if (!normalized) {
      throw new BadRequestException(
        "El nombre del catálogo vehicular es obligatorio",
      );
    }

    let row = await this.findVehicularAny(tipo, normalized, payload);

    if (!row) {
      row = this.catalogoVehicularRepo.create({
        tipo,
        nombre: normalized,
        marca: payload.marca || "",
        modelo: payload.modelo || "",
        motor: payload.motor || "",
        tipoVehiculoId: payload.tipoVehiculoId,
        activo: true,
      } as Partial<CatalogoVehicular>);
      return {
        row: await this.catalogoVehicularRepo.save(row),
        created: true,
        reactivated: false,
      };
    }

    if (!row.activo) {
      row.activo = true;
      row = await this.catalogoVehicularRepo.save(row);
      return { row, created: false, reactivated: true };
    }

    return { row, created: false, reactivated: false };
  }

  private async hasActiveChildren(
    tipo: CatalogoVehicularTipo,
    ctx: {
      marca?: string;
      modelo?: string;
      motor?: string;
      tipoVehiculoId?: number;
    },
  ): Promise<boolean> {
    const qb = this.catalogoVehicularRepo
      .createQueryBuilder("cat")
      .where("cat.tipo = :tipo", { tipo })
      .andWhere("cat.activo = true");

    if (ctx.marca) {
      qb.andWhere("LOWER(cat.marca) = :marca", {
        marca: this.normalizeKey(ctx.marca),
      });
    }

    if (ctx.modelo) {
      qb.andWhere("LOWER(cat.modelo) = :modelo", {
        modelo: this.normalizeKey(ctx.modelo),
      });
    }

    if (ctx.motor) {
      qb.andWhere("LOWER(cat.motor) = :motor", {
        motor: this.normalizeKey(ctx.motor),
      });
    }

    if (ctx.tipoVehiculoId) {
      qb.andWhere("cat.tipoVehiculoId = :tipoVehiculoId", {
        tipoVehiculoId: ctx.tipoVehiculoId,
      });
    }

    return (await qb.getCount()) > 0;
  }

  private async resolveVehicularPayload(
    dto: Partial<CreateCatalogoVehicularDto>,
    tipoBase: CatalogoVehicularTipo,
    existing?: CatalogoVehicular,
  ) {
    const nombre = this.normalize(dto.nombre ?? existing?.nombre);
    if (!nombre) {
      throw new BadRequestException("El nombre es obligatorio");
    }

    const tipoVehiculoId = this.ensureTipoVehiculoId(
      dto.tipoVehiculoId ?? existing?.tipoVehiculoId ?? undefined,
    );
    await this.ensureTipoVehiculoActivo(tipoVehiculoId);

    const marcaInput = this.normalize(dto.marca ?? existing?.marca);
    const modeloInput = this.normalize(dto.modelo ?? existing?.modelo);
    const motorInput = this.normalize(dto.motor ?? existing?.motor);

    if (tipoBase === CatalogoVehicularTipo.MARCA) {
      const dup = await this.findActiveVehicular(
        CatalogoVehicularTipo.MARCA,
        nombre,
        { tipoVehiculoId },
        existing?.id,
      );
      if (dup)
        throw new BadRequestException(
          "La marca ya existe para este tipo de vehículo",
        );
      return { nombre, marca: "", modelo: "", motor: "", tipoVehiculoId };
    }

    if (tipoBase === CatalogoVehicularTipo.MODELO) {
      if (!marcaInput) {
        throw new BadRequestException(
          "Para crear modelo debes seleccionar una marca",
        );
      }

      const marca = await this.findActiveVehicular(
        CatalogoVehicularTipo.MARCA,
        marcaInput,
        { tipoVehiculoId },
      );
      if (!marca) {
        throw new BadRequestException(
          "La marca seleccionada no existe o está inactiva para ese tipo de vehículo",
        );
      }

      const dup = await this.findActiveVehicular(
        CatalogoVehicularTipo.MODELO,
        nombre,
        { marca: marca.nombre, tipoVehiculoId },
        existing?.id,
      );
      if (dup)
        throw new BadRequestException(
          "Ese modelo ya existe para esa marca y tipo de vehículo",
        );

      return {
        nombre,
        marca: marca.nombre,
        modelo: "",
        motor: "",
        tipoVehiculoId,
      };
    }

    if (tipoBase === CatalogoVehicularTipo.MOTOR) {
      if (!marcaInput || !modeloInput) {
        throw new BadRequestException(
          "Para crear motor debes seleccionar marca y modelo",
        );
      }

      const modelo = await this.findActiveVehicular(
        CatalogoVehicularTipo.MODELO,
        modeloInput,
        {
          marca: marcaInput,
          tipoVehiculoId,
        },
      );
      if (!modelo) {
        throw new BadRequestException(
          "El modelo seleccionado no existe para la marca y tipo de vehículo indicado",
        );
      }

      const dup = await this.findActiveVehicular(
        CatalogoVehicularTipo.MOTOR,
        nombre,
        { marca: modelo.marca, modelo: modelo.nombre, tipoVehiculoId },
        existing?.id,
      );
      if (dup)
        throw new BadRequestException(
          "Ese motor ya existe para esa marca, modelo y tipo de vehículo",
        );

      return {
        nombre,
        marca: modelo.marca,
        modelo: modelo.nombre,
        motor: "",
        tipoVehiculoId,
      };
    }

    if (!marcaInput || !modeloInput || !motorInput) {
      throw new BadRequestException(
        "Para crear cilindraje debes seleccionar marca, modelo y motor",
      );
    }

    const motor = await this.findActiveVehicular(
      CatalogoVehicularTipo.MOTOR,
      motorInput,
      {
        marca: marcaInput,
        modelo: modeloInput,
        tipoVehiculoId,
      },
    );

    if (!motor) {
      throw new BadRequestException(
        "El motor seleccionado no existe para esa marca, modelo y tipo de vehículo",
      );
    }

    const dup = await this.findActiveVehicular(
      CatalogoVehicularTipo.CILINDRAJE,
      nombre,
      {
        marca: motor.marca,
        modelo: motor.modelo,
        motor: motor.nombre,
        tipoVehiculoId,
      },
      existing?.id,
    );
    if (dup)
      throw new BadRequestException(
        "Ese cilindraje ya existe para ese motor y tipo de vehículo",
      );

    return {
      nombre,
      marca: motor.marca,
      modelo: motor.modelo,
      motor: motor.nombre,
      tipoVehiculoId,
    };
  }

  async findAllTiposVehiculo(activo = true) {
    return this.tipoVehiculoRepo.find({
      where: activo === undefined ? {} : { activo },
      order: { nombre: "ASC" },
    });
  }

  async createTipoVehiculo(data: Partial<TipoVehiculo>, userId?: number) {
    const tipo = this.tipoVehiculoRepo.create({
      ...data,
      createdBy: userId ?? null,
      updatedBy: userId ?? null,
    });
    return this.tipoVehiculoRepo.save(tipo);
  }

  async updateTipoVehiculo(
    id: number,
    data: Partial<TipoVehiculo>,
    userId?: number,
  ) {
    const tipo = await this.tipoVehiculoRepo.findOne({ where: { id } });
    if (!tipo)
      throw new NotFoundException(`Tipo de vehículo ${id} no encontrado`);
    Object.assign(tipo, data, { updatedBy: userId ?? null });
    return this.tipoVehiculoRepo.save(tipo);
  }

  async inactivateTipoVehiculo(id: number, userId?: number) {
    const tipo = await this.tipoVehiculoRepo.findOne({ where: { id } });
    if (!tipo)
      throw new NotFoundException(`Tipo de vehículo ${id} no encontrado`);
    tipo.activo = false;
    tipo.updatedBy = userId ?? null;
    return this.tipoVehiculoRepo.save(tipo);
  }

  async activateTipoVehiculo(id: number, userId?: number) {
    const tipo = await this.tipoVehiculoRepo.findOne({ where: { id } });
    if (!tipo)
      throw new NotFoundException(`Tipo de vehículo ${id} no encontrado`);
    tipo.activo = true;
    tipo.updatedBy = userId ?? null;
    return this.tipoVehiculoRepo.save(tipo);
  }

  async findAllCosasTrae(activo = true) {
    return this.cosaTraeRepo.find({
      where: activo === undefined ? {} : { activo },
      order: { nombre: "ASC" },
    });
  }

  async createCosaTrae(data: Partial<CosaTrae>) {
    const cosa = this.cosaTraeRepo.create(data);
    return this.cosaTraeRepo.save(cosa);
  }

  async updateCosaTrae(id: number, data: Partial<CosaTrae>) {
    const cosa = await this.cosaTraeRepo.findOne({ where: { id } });
    if (!cosa) throw new NotFoundException(`Cosa que trae ${id} no encontrada`);
    Object.assign(cosa, data);
    return this.cosaTraeRepo.save(cosa);
  }

  async inactivateCosaTrae(id: number) {
    const cosa = await this.cosaTraeRepo.findOne({ where: { id } });
    if (!cosa) throw new NotFoundException(`Cosa que trae ${id} no encontrada`);
    cosa.activo = false;
    return this.cosaTraeRepo.save(cosa);
  }

  async activateCosaTrae(id: number) {
    const cosa = await this.cosaTraeRepo.findOne({ where: { id } });
    if (!cosa) throw new NotFoundException(`Cosa que trae ${id} no encontrada`);
    cosa.activo = true;
    return this.cosaTraeRepo.save(cosa);
  }

  async findAllTiposDano(activo = true) {
    return this.tipoDanoRepo.find({
      where: activo === undefined ? {} : { activo },
      order: { nombre: "ASC" },
    });
  }

  async createTipoDano(data: Partial<TipoDano>) {
    const tipo = this.tipoDanoRepo.create(data);
    return this.tipoDanoRepo.save(tipo);
  }

  async updateTipoDano(id: number, data: Partial<TipoDano>) {
    const tipo = await this.tipoDanoRepo.findOne({ where: { id } });
    if (!tipo) throw new NotFoundException(`Tipo de daño ${id} no encontrado`);
    Object.assign(tipo, data);
    return this.tipoDanoRepo.save(tipo);
  }

  async inactivateTipoDano(id: number) {
    const tipo = await this.tipoDanoRepo.findOne({ where: { id } });
    if (!tipo) throw new NotFoundException(`Tipo de daño ${id} no encontrado`);
    tipo.activo = false;
    return this.tipoDanoRepo.save(tipo);
  }

  async activateTipoDano(id: number) {
    const tipo = await this.tipoDanoRepo.findOne({ where: { id } });
    if (!tipo) throw new NotFoundException(`Tipo de daño ${id} no encontrado`);
    tipo.activo = true;
    return this.tipoDanoRepo.save(tipo);
  }

  async listCatalogosVehiculares(
    tipo: CatalogoVehicularTipo,
    filters?: {
      marca?: string;
      modelo?: string;
      motor?: string;
      tipoVehiculoId?: number;
      activo?: boolean;
    },
  ) {
    const qb = this.catalogoVehicularRepo
      .createQueryBuilder("cat")
      .where("cat.tipo = :tipo", { tipo });

    if (filters?.activo !== undefined) {
      qb.andWhere("cat.activo = :activo", { activo: filters.activo });
    }

    if (filters?.marca)
      qb.andWhere("LOWER(cat.marca) = :marca", {
        marca: this.normalizeKey(filters.marca),
      });
    if (filters?.modelo)
      qb.andWhere("LOWER(cat.modelo) = :modelo", {
        modelo: this.normalizeKey(filters.modelo),
      });
    if (filters?.motor)
      qb.andWhere("LOWER(cat.motor) = :motor", {
        motor: this.normalizeKey(filters.motor),
      });
    if (filters?.tipoVehiculoId)
      qb.andWhere("cat.tipoVehiculoId = :tipoVehiculoId", {
        tipoVehiculoId: filters.tipoVehiculoId,
      });

    return qb.orderBy("cat.nombre", "ASC").getMany();
  }

  async createCatalogoVehicular(dto: CreateCatalogoVehicularDto) {
    const payload = await this.resolveVehicularPayload(dto, dto.tipo);

    const row = this.catalogoVehicularRepo.create({
      tipo: dto.tipo,
      nombre: payload.nombre,
      marca: payload.marca,
      modelo: payload.modelo,
      motor: payload.motor,
      tipoVehiculoId: payload.tipoVehiculoId,
      activo: true,
    } as Partial<CatalogoVehicular>);

    return this.catalogoVehicularRepo.save(row);
  }

  async updateCatalogoVehicular(
    id: number,
    dto: Partial<CreateCatalogoVehicularDto>,
  ) {
    const row = await this.catalogoVehicularRepo.findOne({ where: { id } });
    if (!row)
      throw new NotFoundException(`Catalogo vehicular ${id} no encontrado`);

    if (dto.tipo && dto.tipo !== row.tipo) {
      throw new BadRequestException("No se puede cambiar el tipo del registro");
    }

    const payload = await this.resolveVehicularPayload(dto, row.tipo, row);

    row.nombre = payload.nombre;
    row.marca = payload.marca;
    row.modelo = payload.modelo;
    row.motor = payload.motor;
    row.tipoVehiculoId = payload.tipoVehiculoId;

    return this.catalogoVehicularRepo.save(row);
  }

  async inactivateCatalogoVehicular(id: number) {
    const row = await this.catalogoVehicularRepo.findOne({ where: { id } });
    if (!row)
      throw new NotFoundException(`Catalogo vehicular ${id} no encontrado`);

    if (row.tipo === CatalogoVehicularTipo.MARCA) {
      const hasChildren = await this.hasActiveChildren(
        CatalogoVehicularTipo.MODELO,
        {
          marca: row.nombre,
          tipoVehiculoId: row.tipoVehiculoId ?? undefined,
        },
      );
      if (hasChildren) {
        throw new BadRequestException(
          "No puedes inactivar una marca que tiene modelos activos",
        );
      }
    }

    if (row.tipo === CatalogoVehicularTipo.MODELO) {
      const hasChildren = await this.hasActiveChildren(
        CatalogoVehicularTipo.MOTOR,
        {
          marca: row.marca,
          modelo: row.nombre,
          tipoVehiculoId: row.tipoVehiculoId ?? undefined,
        },
      );
      if (hasChildren) {
        throw new BadRequestException(
          "No puedes inactivar un modelo que tiene motores activos",
        );
      }
    }

    if (row.tipo === CatalogoVehicularTipo.MOTOR) {
      const hasChildren = await this.hasActiveChildren(
        CatalogoVehicularTipo.CILINDRAJE,
        {
          marca: row.marca,
          modelo: row.modelo,
          motor: row.nombre,
          tipoVehiculoId: row.tipoVehiculoId ?? undefined,
        },
      );
      if (hasChildren) {
        throw new BadRequestException(
          "No puedes inactivar un motor que tiene cilindrajes activos",
        );
      }
    }

    row.activo = false;
    return this.catalogoVehicularRepo.save(row);
  }

  async activateCatalogoVehicular(id: number) {
    const row = await this.catalogoVehicularRepo.findOne({ where: { id } });
    if (!row)
      throw new NotFoundException(`Catalogo vehicular ${id} no encontrado`);

    row.activo = true;
    return this.catalogoVehicularRepo.save(row);
  }

  async importVehicularExcel(buffer: Buffer, userId?: number) {
    const workbook = new ExcelJS.Workbook();
    try {
      await workbook.xlsx.load(buffer as any);
    } catch {
      throw new BadRequestException("No se pudo leer el archivo Excel");
    }

    const worksheet = workbook.worksheets[0];
    if (!worksheet) {
      throw new BadRequestException("El archivo Excel no tiene hojas");
    }

    const headerRow = worksheet.getRow(1);
    const headers: string[] = [];
    headerRow.eachCell({ includeEmpty: true }, (cell, colNumber) => {
      headers[colNumber] = this.normalizeHeader(cell.text);
    });

    const rows = Array.from(
      { length: Math.max(worksheet.rowCount - 1, 0) },
      (_, index) => index + 2,
    );

    if (rows.length === 0) {
      throw new BadRequestException("El archivo Excel no tiene datos");
    }

    const stats = {
      totalRows: rows.length,
      processedRows: 0,
      skippedRows: 0,
      created: {
        tipos: 0,
        marcas: 0,
        modelos: 0,
        motores: 0,
        cilindrajes: 0,
      },
      reactivated: {
        tipos: 0,
        marcas: 0,
        modelos: 0,
        motores: 0,
        cilindrajes: 0,
      },
      existing: {
        tipos: 0,
        marcas: 0,
        modelos: 0,
        motores: 0,
        cilindrajes: 0,
      },
      errors: [] as Array<{ row: number; message: string }>,
    };

    const aliases: Record<string, keyof typeof stats.created> = {
      tipo: "tipos",
      tipos: "tipos",
      tipovehiculo: "tipos",
      tiposvehiculo: "tipos",
      tipodevehiculo: "tipos",
      marca: "marcas",
      marcas: "marcas",
      linea: "modelos",
      lineas: "modelos",
      modelo: "modelos",
      modelos: "modelos",
      motor: "motores",
      motores: "motores",
      seriemotor: "motores",
      cilindraje: "cilindrajes",
      cilindrajes: "cilindrajes",
      cc: "cilindrajes",
    };

    const hasHeader = (field: keyof typeof stats.created) =>
      headers.some((header) => aliases[header] === field);

    if (!hasHeader("tipos") || !hasHeader("marcas") || !hasHeader("modelos")) {
      throw new BadRequestException(
        "El Excel debe incluir encabezados obligatorios: TIPO, MARCA y LINEA",
      );
    }

    const readValue = (
      rowNumber: number,
      field: keyof typeof stats.created,
    ) => {
      const row = worksheet.getRow(rowNumber);
      for (let colNumber = 1; colNumber <= headers.length; colNumber += 1) {
        if (aliases[headers[colNumber]] === field) {
          return this.normalize(row.getCell(colNumber).text);
        }
      }
      return "";
    };

    const track = (
      field: keyof typeof stats.created,
      result: { created: boolean; reactivated: boolean },
    ) => {
      if (result.created) stats.created[field] += 1;
      else if (result.reactivated) stats.reactivated[field] += 1;
      else stats.existing[field] += 1;
    };

    for (const rowNumber of rows) {
      const tipoNombre = readValue(rowNumber, "tipos");
      const marcaNombre = readValue(rowNumber, "marcas");
      const modeloNombre = readValue(rowNumber, "modelos");
      const motorNombre = readValue(rowNumber, "motores");
      const cilindrajeNombre = readValue(rowNumber, "cilindrajes");

      if (
        !tipoNombre &&
        !marcaNombre &&
        !modeloNombre &&
        !motorNombre &&
        !cilindrajeNombre
      ) {
        stats.skippedRows += 1;
        continue;
      }

      try {
        if (!tipoNombre) {
          throw new BadRequestException("Tipo de vehículo es obligatorio");
        }

        if (!marcaNombre) {
          throw new BadRequestException("Marca es obligatoria");
        }

        if (!modeloNombre) {
          throw new BadRequestException("Linea es obligatoria");
        }

        const tipo = await this.ensureTipoVehiculoByName(tipoNombre, userId);
        track("tipos", tipo);

        const marca = await this.ensureVehicularByName(
          CatalogoVehicularTipo.MARCA,
          marcaNombre,
          { tipoVehiculoId: tipo.row.id },
        );
        track("marcas", marca);

        if (modeloNombre) {
          const modelo = await this.ensureVehicularByName(
            CatalogoVehicularTipo.MODELO,
            modeloNombre,
            {
              marca: marca.row.nombre,
              tipoVehiculoId: tipo.row.id,
            },
          );
          track("modelos", modelo);

          if (motorNombre) {
            const motor = await this.ensureVehicularByName(
              CatalogoVehicularTipo.MOTOR,
              motorNombre,
              {
                marca: marca.row.nombre,
                modelo: modelo.row.nombre,
                tipoVehiculoId: tipo.row.id,
              },
            );
            track("motores", motor);

            if (cilindrajeNombre) {
              const cilindraje = await this.ensureVehicularByName(
                CatalogoVehicularTipo.CILINDRAJE,
                cilindrajeNombre,
                {
                  marca: marca.row.nombre,
                  modelo: modelo.row.nombre,
                  motor: motor.row.nombre,
                  tipoVehiculoId: tipo.row.id,
                },
              );
              track("cilindrajes", cilindraje);
            }
          } else if (cilindrajeNombre) {
            throw new BadRequestException(
              "Para crear cilindraje tambien debes enviar motor",
            );
          }
        } else if (motorNombre || cilindrajeNombre) {
          throw new BadRequestException(
            "Para crear motor o cilindraje tambien debes enviar modelo",
          );
        }

        stats.processedRows += 1;
      } catch (error: any) {
        stats.errors.push({
          row: rowNumber,
          message: error?.message || "Fila no valida",
        });
      }
    }

    return stats;
  }

  async seedCatalogos() {
    const tiposCount = await this.tipoVehiculoRepo.count();
    if (tiposCount === 0) {
      await this.tipoVehiculoRepo.save([
        { nombre: "Carro", plantillaDanos: "carro" },
        { nombre: "Moto", plantillaDanos: "moto" },
        { nombre: "Camion", plantillaDanos: "camion" },
        { nombre: "Pickup", plantillaDanos: "carro" },
        { nombre: "SUV", plantillaDanos: "carro" },
      ]);
    }

    const cosasCount = await this.cosaTraeRepo.count();
    if (cosasCount === 0) {
      await this.cosaTraeRepo.save([
        { nombre: "Radio" },
        { nombre: "Gata" },
        { nombre: "Herramientas" },
        { nombre: "Llanta de repuesto" },
        { nombre: "Triangulos" },
        { nombre: "Extintor" },
      ]);
    }

    const danosCount = await this.tipoDanoRepo.count();
    if (danosCount === 0) {
      await this.tipoDanoRepo.save([
        { nombre: "Golpe", icono: "golpe", color: "#ef4444" },
        { nombre: "Rayon", icono: "rayon", color: "#f59e0b" },
        { nombre: "Quebrado", icono: "quebrado", color: "#dc2626" },
      ]);
    }

    const tiposVehiculo = await this.tipoVehiculoRepo.find({
      where: { activo: true },
    });
    const tipoByName = new Map(
      tiposVehiculo.map((tipo) => [this.normalizeKey(tipo.nombre), tipo]),
    );
    const vehicularSeed = [
      {
        tipoVehiculo: "Carro",
        marcas: {
          Toyota: ["COROLLA", "YARIS", "RAV4", "HILUX"],
          Honda: ["CIVIC", "CR-V", "HR-V"],
          Nissan: ["SENTRA", "VERSA", "FRONTIER"],
          Mazda: ["MAZDA 2", "MAZDA 3", "CX-5"],
          Hyundai: ["ACCENT", "ELANTRA", "TUCSON"],
          Kia: ["RIO", "SPORTAGE", "SORENTO"],
          Mitsubishi: ["L200", "OUTLANDER", "MIRAGE"],
        },
      },
      {
        tipoVehiculo: "Pickup",
        marcas: {
          Toyota: ["HILUX", "TACOMA"],
          Nissan: ["FRONTIER", "NP300"],
          Mitsubishi: ["L200"],
          Isuzu: ["D-MAX"],
          Ford: ["RANGER", "F-150"],
        },
      },
      {
        tipoVehiculo: "Moto",
        marcas: {
          Honda: ["CG 125", "CB 190", "XR 150"],
          Yamaha: ["FZ", "YBR 125", "XTZ"],
          Suzuki: ["GN 125", "GIXXER"],
          Bajaj: ["PULSAR", "BOXER", "DISCOVER"],
        },
      },
      {
        tipoVehiculo: "Camion",
        marcas: {
          Hino: ["300", "500"],
          Isuzu: ["NPR", "NQR"],
          Freightliner: ["M2", "CASCADIA"],
        },
      },
    ];

    let vehicularCreated = 0;
    for (const group of vehicularSeed) {
      const tipoVehiculo = tipoByName.get(
        this.normalizeKey(group.tipoVehiculo),
      );
      if (!tipoVehiculo) continue;

      for (const [marcaNombre, modelos] of Object.entries(group.marcas)) {
        const marca = await this.findActiveVehicular(
          CatalogoVehicularTipo.MARCA,
          marcaNombre,
          { tipoVehiculoId: tipoVehiculo.id },
        );
        const savedMarca =
          marca ||
          (await this.catalogoVehicularRepo.save({
            tipo: CatalogoVehicularTipo.MARCA,
            nombre: marcaNombre,
            marca: "",
            modelo: "",
            motor: "",
            tipoVehiculoId: tipoVehiculo.id,
          }));

        if (!marca) vehicularCreated += 1;

        for (const modeloNombre of modelos) {
          const modelo = await this.findActiveVehicular(
            CatalogoVehicularTipo.MODELO,
            modeloNombre,
            { marca: savedMarca.nombre, tipoVehiculoId: tipoVehiculo.id },
          );

          if (!modelo) {
            await this.catalogoVehicularRepo.save({
              tipo: CatalogoVehicularTipo.MODELO,
              nombre: modeloNombre,
              marca: savedMarca.nombre,
              modelo: "",
              motor: "",
              tipoVehiculoId: tipoVehiculo.id,
            });
            vehicularCreated += 1;
          }
        }
      }
    }

    return { ok: true, vehicularCreated };
  }
}
