import { PaginatedResponse } from '@edr/types';
import { Injectable } from '@nestjs/common';
import { DataSource, FindManyOptions, Repository } from 'typeorm';
import { paginateQuery } from '../../../common/utils/pagination.util';
import { ListCargoTypesQueryDto } from '../dto/list-rule-engine-query.dto';
import { CargoType } from '../entities/cargo-type.entity';
import { ICargoTypesRepository } from '../interfaces/cargo-types.repository.interface';

@Injectable()
export class CargoTypesRepository implements ICargoTypesRepository {
  private readonly repo: Repository<CargoType>;

  constructor(private readonly dataSource: DataSource) {
    this.repo = this.dataSource.getRepository(CargoType);
  }

  findById(id: string): Promise<CargoType | null> {
    return this.repo.findOne({ where: { id }, relations: { parent: true, wagonTypes: true } });
  }

  findByCode(code: string): Promise<CargoType | null> {
    return this.repo.findOne({ where: { code } });
  }

  findAll(options?: FindManyOptions<CargoType>): Promise<CargoType[]> {
    return this.repo.find(options);
  }

  findAndCount(options?: FindManyOptions<CargoType>): Promise<[CargoType[], number]> {
    return this.repo.findAndCount(options);
  }

  /** Paged list with server-side search (name/code) in the standard envelope. */
  findPaged(query: ListCargoTypesQueryDto): Promise<PaginatedResponse<CargoType>> {
    const qb = this.repo
      .createQueryBuilder('cargoType')
      .leftJoinAndSelect('cargoType.parent', 'parent')
      .leftJoinAndSelect('cargoType.wagonTypes', 'wagonType')
      .orderBy(`cargoType.${query.sortBy ?? 'displayOrder'}`, query.sortOrder ?? 'ASC');

    if (query.isActive !== undefined) {
      qb.andWhere('cargoType.isActive = :isActive', { isActive: query.isActive });
    }
    if (query.requiresDirectorApproval !== undefined) {
      qb.andWhere('cargoType.requiresDirectorApproval = :requiresDirectorApproval', {
        requiresDirectorApproval: query.requiresDirectorApproval,
      });
    }
    if (query.parentGroupId !== undefined) {
      qb.andWhere('cargoType.parentGroupId = :parentGroupId', { parentGroupId: query.parentGroupId });
    }
    if (query.search) {
      qb.andWhere('(cargoType.cargoTypeName ILIKE :search OR cargoType.code ILIKE :search)', {
        search: `%${query.search}%`,
      });
    }

    return paginateQuery(qb, query);
  }

  async create(data: Partial<CargoType>): Promise<CargoType> {
    const entity = this.repo.create(data);
    return this.repo.save(entity);
  }

  async update(id: string, data: Partial<CargoType>): Promise<CargoType | null> {
    // Relation lists can't ride a column UPDATE — sync them via entity save.
    const { wagonTypes, ...columns } = data;
    if (Object.keys(columns).length) {
      await this.repo.update(id, columns as never);
    }
    if (wagonTypes) {
      const entity = await this.repo.findOne({ where: { id } });
      if (entity) {
        entity.wagonTypes = wagonTypes;
        await this.repo.save(entity);
      }
    }
    return this.findById(id);
  }

  async softDelete(id: string): Promise<void> {
    await this.repo.softDelete(id);
  }
}
