import { BaseRepository } from '@edr/api-common';
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { EntityManager, Repository } from 'typeorm';

import { TrainSchedule, TrainScheduleStatus } from './entities/train-schedule.entity';

@Injectable()
export class TrainSchedulesRepository extends BaseRepository<TrainSchedule> {
  constructor(
    @InjectRepository(TrainSchedule)
    repository: Repository<TrainSchedule>,
  ) {
    super(repository);
  }

  private repo(manager?: EntityManager) {
    return manager ? manager.getRepository(TrainSchedule) : this.repository;
  }

  findByIdWithFullGraph(id: string, manager?: EntityManager): Promise<TrainSchedule | null> {
    return this.repo(manager).findOne({
      where: { id },
      // One SELECT per relation instead of a single monster join — the nested
      // wagon×allocation×booking×container branches multiply rows catastrophically
      // when joined (measured ~925ms vs ~84ms on a 21-wagon schedule).
      relationLoadStrategy: 'query',
      relations: {
        // Yards carry the route's display name; without them formatRouteLabel
        // degrades to the literal "Origin → Destination". Milestones (with
        // their yards) give it the full corridor path.
        route: { originYard: true, destinationYard: true, milestones: { yard: true } },
        trainSet: {
          locomotive: true,
          locomotives: { locomotive: true },
          train: true,
          wagons: {
            wagonType: true,
            physicalWagon: true,
            allocations: {
              booking: { company: true, bookingContainers: { containerType: true } },
              containerItems: true,
            },
          },
        },
        originStation: true,
        destinationStation: true,
        scheduleBookings: {
          booking: {
            company: true,
            originYard: true,
            destinationYard: true,
            // wagonTypes feed grossBookingWeightTons the REAL tare of the
            // wagon type the booking rides — without them it falls back to
            // default tares and the workspace gross drifts from the validator.
            bookingContainers: { containerType: { wagonTypes: true } },
            cargoType: { wagonTypes: true },
          },
        },
      },
    });
  }

  /**
   * Light fetch for human-facing labels (notifications): reference, train
   * number, departure and the two station names — none of the composition
   * graph {@link findByIdWithFullGraph} drags in.
   */
  findByIdWithStations(id: string): Promise<TrainSchedule | null> {
    return this.repository.findOne({
      where: { id },
      relations: { originStation: true, destinationStation: true },
    });
  }

  async updateStatus(
    id: string,
    status: TrainScheduleStatus,
    extra?: Partial<TrainSchedule>,
    manager?: EntityManager,
  ): Promise<void> {
    await this.repo(manager).update(id, { status, ...extra } as never);
  }

  /**
   * Highest NNNNN sequence already issued for `S-<year>-…` references. Includes
   * soft-deleted rows so the next number never reuses one still occupying the
   * unique index (see the same pattern on BookingsRepository).
   */
  async maxReferenceSequence(year: number): Promise<number> {
    const row = await this.repository
      .createQueryBuilder('schedule')
      .withDeleted()
      .select(
        "COALESCE(MAX(CAST(SUBSTRING(schedule.reference FROM '[0-9]+$') AS int)), 0)",
        'max',
      )
      .where('schedule.reference LIKE :prefix', { prefix: `S-${year}-%` })
      .getRawOne<{ max: string | number | null }>();
    return Number(row?.max ?? 0);
  }
}
