import { MigrationInterface, QueryRunner } from 'typeorm';

/**
 * Re-seed the EDR wagon fleet onto the official ER numbering.
 *
 * Supersedes SeedWagonsWithYardAssignment1784000000001, which seeded 500 wagons
 * on a `<CODE>-NNNN` scheme and wrote the status as 'Available' — mixed case
 * that never matches WagonStatus.Available ('AVAILABLE'), so status filters
 * silently returned nothing. This seed uses the enum value.
 *
 * Every wagon lands unassigned: current_yard_id NULL, status AVAILABLE. Wagon
 * specs (capacity/length/tare) stay owned by wagon_types and are not touched —
 * the types already exist and only the wagon↔type link is (re)established here.
 */
type FleetRow = {
  code: string;
  start: number;
  end: number;
  count: number;
};

/** Official fleet: 1100 wagons, ER0001–ER1100, contiguous across 10 types. */
const FLEET: FleetRow[] = [
  { code: 'PW2', start: 1, end: 220, count: 220 },
  { code: 'CW4', start: 221, end: 330, count: 110 },
  { code: 'CW3', start: 331, end: 350, count: 20 },
  { code: 'KW2', start: 351, end: 370, count: 20 },
  { code: 'KW3', start: 371, end: 390, count: 20 },
  { code: 'NW5', start: 391, end: 940, count: 550 },
  { code: 'BW1', start: 941, end: 950, count: 10 },
  { code: 'GW2', start: 951, end: 1060, count: 110 },
  { code: 'NW6', start: 1061, end: 1080, count: 20 },
  { code: 'NW7', start: 1081, end: 1100, count: 20 },
];

const wagonNumber = (sequence: number) => `ER${String(sequence).padStart(4, '0')}`;

export class SeedEdrWagonFleetErNumbering2260000000000 implements MigrationInterface {
  name = 'SeedEdrWagonFleetErNumbering2260000000000';

  public async up(queryRunner: QueryRunner): Promise<void> {
    // Full replacement: the ER range is the fleet of record, so any wagon
    // outside it is stale seed data. Safe to hard-delete — containers and
    // train_set_wagons null their link, wagon_movements cascade.
    await queryRunner.query(`DELETE FROM freight.wagons;`);

    // Deliberately does NOT create a unique index on wagon_number. It once did,
    // to satisfy an ON CONFLICT clause that no longer exists (the DELETE above
    // makes collisions impossible). Recreating the plain index here would undo
    // WagonNumberPartialUnique2280000000000, which replaces it with a PARTIAL
    // unique index so soft-deleted wagons stop reserving their number — this
    // seeder is run directly by scripts/seed-edr-wagons.ts, which would
    // otherwise resurrect the plain index on an already-migrated database.

    for (const row of FLEET) {
      if (row.end - row.start + 1 !== row.count) {
        throw new Error(`wagon_range_mismatch:${row.code}`);
      }

      const [typeRecord] = await queryRunner.query(
        `SELECT id FROM freight.wagon_types WHERE code = $1 AND deleted_at IS NULL LIMIT 1;`,
        [row.code],
      );

      if (!typeRecord?.id) {
        throw new Error(`wagon_type_missing:${row.code}`);
      }

      // generate_series builds the range server-side — one round trip per type
      // instead of 1100 individual INSERTs. No ON CONFLICT clause: every wagon
      // was deleted above, so a plain INSERT cannot collide, and the clause would
      // otherwise hard-require a unique index this table lacks on some envs.
      await queryRunner.query(
        `
          INSERT INTO freight.wagons (
            wagon_number,
            wagon_type_id,
            status,
            current_yard_id,
            train_id,
            sequence_number,
            notes,
            train_set_wagon_id,
            current_train_schedule_id
          )
          SELECT
            'ER' || LPAD(seq::text, 4, '0'),
            $1::uuid,
            'AVAILABLE',
            NULL,
            NULL,
            NULL,
            NULL,
            NULL,
            NULL
          FROM generate_series($2::int, $3::int) AS seq;
        `,
        [typeRecord.id, row.start, row.end],
      );
    }
  }

  public async down(queryRunner: QueryRunner): Promise<void> {
    await queryRunner.query(
      `DELETE FROM freight.wagons WHERE wagon_number BETWEEN $1 AND $2;`,
      [wagonNumber(FLEET[0].start), wagonNumber(FLEET[FLEET.length - 1].end)],
    );
  }
}
