/**
 * TypeORM Port Repository
 *
 * Implements PortRepository interface using TypeORM
 */

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Port } from '@domain/entities/port.entity';
import { PortRepository } from '@domain/ports/out/port.repository';
import { PortOrmEntity } from '../entities/port.orm-entity';
import { PortOrmMapper } from '../mappers/port-orm.mapper';

@Injectable()
export class TypeOrmPortRepository implements PortRepository {
  constructor(
    @InjectRepository(PortOrmEntity)
    private readonly repository: Repository<PortOrmEntity>
  ) {}

  async save(port: Port): Promise<Port> {
    const orm = PortOrmMapper.toOrm(port);
    const saved = await this.repository.save(orm);
    return PortOrmMapper.toDomain(saved);
  }

  async saveMany(ports: Port[]): Promise<Port[]> {
    const orms = ports.map(port => PortOrmMapper.toOrm(port));
    const saved = await this.repository.save(orms);
    return PortOrmMapper.toDomainMany(saved);
  }

  async findByCode(code: string): Promise<Port | null> {
    const orm = await this.repository.findOne({
      where: { code: code.toUpperCase() },
    });
    return orm ? PortOrmMapper.toDomain(orm) : null;
  }

  async findByCodes(codes: string[]): Promise<Port[]> {
    const upperCodes = codes.map(c => c.toUpperCase());
    const orms = await this.repository
      .createQueryBuilder('port')
      .where('port.code IN (:...codes)', { codes: upperCodes })
      .getMany();
    return PortOrmMapper.toDomainMany(orms);
  }

  async search(query: string, limit = 10, countryFilter?: string): Promise<Port[]> {
    const qb = this.repository
      .createQueryBuilder('port')
      .where('port.is_active = :isActive', { isActive: true });

    // Fuzzy search using pg_trgm (trigram similarity)
    // First try exact match on code
    qb.andWhere('(port.code ILIKE :code OR port.name ILIKE :name OR port.city ILIKE :city)', {
      code: `${query}%`,
      name: `%${query}%`,
      city: `%${query}%`,
    });

    if (countryFilter) {
      qb.andWhere('port.country = :country', { country: countryFilter.toUpperCase() });
    }

    // Order by relevance: exact code match first, then name, then city
    qb.orderBy(
      `CASE
        WHEN port.code ILIKE :exactCode THEN 1
        WHEN port.name ILIKE :exactName THEN 2
        WHEN port.code ILIKE :startCode THEN 3
        WHEN port.name ILIKE :startName THEN 4
        ELSE 5
      END`,
      'ASC'
    );
    qb.setParameters({
      exactCode: query.toUpperCase(),
      exactName: query,
      startCode: `${query.toUpperCase()}%`,
      startName: `${query}%`,
    });

    qb.limit(limit);

    const orms = await qb.getMany();
    return PortOrmMapper.toDomainMany(orms);
  }

  async findAllActive(): Promise<Port[]> {
    const orms = await this.repository.find({
      where: { isActive: true },
      order: { name: 'ASC' },
    });
    return PortOrmMapper.toDomainMany(orms);
  }

  async findByCountry(countryCode: string): Promise<Port[]> {
    const orms = await this.repository.find({
      where: { country: countryCode.toUpperCase(), isActive: true },
      order: { name: 'ASC' },
    });
    return PortOrmMapper.toDomainMany(orms);
  }

  async count(): Promise<number> {
    return this.repository.count();
  }

  async deleteByCode(code: string): Promise<void> {
    await this.repository.delete({ code: code.toUpperCase() });
  }
}
