import { PaginatedResponse } from '@edr/types';
import {
  BadRequestException,
  ConflictException,
  Inject,
  Injectable,
  NotFoundException,
} from '@nestjs/common';
import { CreateYardDistanceDto } from '../dto/create-yard-distance.dto';
import { ListYardDistancesQueryDto } from '../dto/list-rule-engine-query.dto';
import { UpdateYardDistanceDto } from '../dto/update-yard-distance.dto';
import { YardDistance } from '../entities/yard-distance.entity';
import {
  IYardDistancesRepository,
  YARD_DISTANCES_REPOSITORY,
} from '../interfaces/yard-distances.repository.interface';
import { IYardsRepository, YARDS_REPOSITORY } from '../interfaces/yards.repository.interface';

/**
 * Flat row shape for the backoffice config table: the yard relations stay for
 * API consumers, plus label fields the generic rule-engine grid can render.
 */
export type YardDistanceRow = YardDistance & {
  fromYardLabel: string;
  toYardLabel: string;
};

const yardDisplay = (yard?: { label?: string; code?: string } | null): string =>
  yard?.label ?? yard?.code ?? '—';

const toRow = (entity: YardDistance): YardDistanceRow =>
  Object.assign(entity, {
    fromYardLabel: yardDisplay(entity.fromYard),
    toYardLabel: yardDisplay(entity.toYard),
  });

@Injectable()
export class YardDistancesService {
  constructor(
    @Inject(YARD_DISTANCES_REPOSITORY)
    private readonly repository: IYardDistancesRepository,
    @Inject(YARDS_REPOSITORY)
    private readonly yardsRepository: IYardsRepository,
  ) {}

  async findAll(query: ListYardDistancesQueryDto): Promise<PaginatedResponse<YardDistanceRow>> {
    const page = await this.repository.findPaged(query);
    return { ...page, items: page.items.map(toRow) };
  }

  async findById(id: string): Promise<YardDistanceRow> {
    const entity = await this.repository.findById(id);
    if (!entity) throw new NotFoundException(`Yard distance ${id} not found`);
    return toRow(entity);
  }

  async create(dto: CreateYardDistanceDto): Promise<YardDistanceRow> {
    await this.assertValidPair(dto.fromYardId, dto.toYardId);

    const created = await this.repository.create({
      fromYardId: dto.fromYardId,
      toYardId: dto.toYardId,
      distanceKm: dto.distanceKm.toFixed(2),
    });
    return toRow(created);
  }

  async update(id: string, dto: UpdateYardDistanceDto): Promise<YardDistanceRow> {
    const existing = await this.findById(id);

    const fromYardId = dto.fromYardId ?? existing.fromYardId;
    const toYardId = dto.toYardId ?? existing.toYardId;
    if (fromYardId !== existing.fromYardId || toYardId !== existing.toYardId) {
      await this.assertValidPair(fromYardId, toYardId, id);
    }

    const updated = await this.repository.update(id, {
      fromYardId,
      toYardId,
      ...(dto.distanceKm != null ? { distanceKm: dto.distanceKm.toFixed(2) } : {}),
    });
    if (!updated) throw new NotFoundException(`Yard distance ${id} not found`);
    return toRow(updated);
  }

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

  /**
   * Both yards must exist and differ, and the pair must not already be
   * configured in either direction — distances are symmetric, so an A→B row
   * already covers B→A.
   */
  private async assertValidPair(
    fromYardId: string,
    toYardId: string,
    ignoreId?: string,
  ): Promise<void> {
    if (fromYardId === toYardId) {
      throw new BadRequestException('From and to yards must be different');
    }

    const [fromYard, toYard] = await Promise.all([
      this.yardsRepository.findById(fromYardId),
      this.yardsRepository.findById(toYardId),
    ]);
    if (!fromYard) throw new BadRequestException(`Yard ${fromYardId} does not exist`);
    if (!toYard) throw new BadRequestException(`Yard ${toYardId} does not exist`);

    const existing = await this.repository.findBetween(fromYardId, toYardId);
    if (existing && existing.id !== ignoreId) {
      throw new ConflictException(
        `A distance between ${fromYard.label} and ${toYard.label} is already configured`,
      );
    }
  }
}
