import { Injectable, Logger } from '@nestjs/common';
import { DataSource } from 'typeorm';

import { Booking } from '../modules/bookings/entities/booking.entity';
import { CargoType } from '../modules/rule-engine/entities/cargo-type.entity';
import { Locomotive } from '../modules/locomotives/entities/locomotive.entity';
import { ServiceType } from '../modules/rule-engine/entities/service-type.entity';
import { Yard } from '../modules/rule-engine/entities/yard.entity';
import { TrainSchedule } from '../modules/train-schedules/entities/train-schedule.entity';
import { TrainScheduleBooking } from '../modules/train-schedules/entities/train-schedule-booking.entity';
import { TrainSet } from '../modules/train-sets/entities/train-set.entity';
import { Warehouse } from '../modules/warehouses/entities/warehouse.entity';
import { WarehouseInventory } from '../modules/warehouses/entities/warehouse-inventory.entity';
import { WarehouseYard } from '../modules/warehouses/entities/warehouse-yard.entity';
import { WarehouseZone } from '../modules/warehouses/entities/warehouse-zone.entity';

/**
 * One coherent warehouse dataset so EVERY queue/tab shows representative data:
 *   Export → Receive Queue   : PAID export bookings, not yet received
 *   Export → Ready To Load   : EXPORT inventory READY_FOR_LOADING + inspection PASSED
 *   Export → Loaded/Dispatch : EXPORT inventory LOADED
 *   Import → Arrive Queue    : an ARRIVED import train with IN_TRANSIT bookings (no inventory)
 *   Import → Unloaded Queue  : UNLOADED import inventory
 *   Import → Dispatch Queue  : READY_FOR_PICKUP import inventory (PASSED)
 *
 * Idempotent: guarded on a sentinel booking reference. Uses dedicated WH-DEMO-* references so it
 * never collides with other seeders. To repopulate after items are walked through their lifecycle,
 * delete the WH-DEMO-* bookings (cascades) and reboot.
 */
@Injectable()
export class WarehouseDemoSeeder {
  private readonly logger = new Logger(WarehouseDemoSeeder.name);
  private readonly SENTINEL = 'WH-DEMO-RCV-1';

  constructor(private readonly dataSource: DataSource) {}

  async run(): Promise<void> {
    const bookingRepo = this.dataSource.getRepository(Booking);
    if (await bookingRepo.findOne({ where: { reference: this.SENTINEL } })) {
      this.logger.log('Warehouse demo data already seeded, skipping');
      return;
    }

    try {
      const yardRepo = this.dataSource.getRepository(Yard);
      const serviceTypeRepo = this.dataSource.getRepository(ServiceType);
      const cargoTypeRepo = this.dataSource.getRepository(CargoType);
      const warehouseRepo = this.dataSource.getRepository(Warehouse);
      const whYardRepo = this.dataSource.getRepository(WarehouseYard);
      const whZoneRepo = this.dataSource.getRepository(WarehouseZone);
      const invRepo = this.dataSource.getRepository(WarehouseInventory);

      const djibYard =
        (await yardRepo.findOne({ where: { code: 'DJIB_PORT' } })) ??
        (await yardRepo.findOne({ where: { country: 'Djibouti' } }));
      const ethYard =
        (await yardRepo.findOne({ where: { code: 'MOJO' } })) ??
        (await yardRepo.findOne({ where: { country: 'Ethiopia' } }));
      const serviceType =
        (await serviceTypeRepo.findOne({ where: { code: 'RAIL_CONTAINER' } })) ??
        (await serviceTypeRepo.findOne({ where: { isActive: true } }));
      const cargoType = await cargoTypeRepo.findOne({ where: { isActive: true } });

      if (!djibYard || !ethYard || !serviceType) {
        this.logger.warn(
          `Missing yards/service type (djib=${djibYard?.code}, eth=${ethYard?.code}, svc=${serviceType?.code}); skipping`,
        );
        return;
      }

      const warehouse = await warehouseRepo.findOne({ where: { code: 'INDODE_OPEN' } });
      const whYard = warehouse ? await whYardRepo.findOne({ where: { warehouseId: warehouse.id } }) : null;
      const whZone = whYard ? await whZoneRepo.findOne({ where: { yardId: whYard.id } }) : null;
      if (!warehouse || !whYard || !whZone) {
        this.logger.warn('INDODE_OPEN warehouse/yard/zone missing; skipping warehouse demo seed');
        return;
      }

      const now = Date.now();
      const ago = (mins: number) => new Date(now - mins * 60_000);

      // EXPORT booking = Ethiopia → Djibouti; IMPORT booking = Djibouti → Ethiopia.
      const makeBooking = async (
        reference: string,
        direction: 'EXPORT' | 'IMPORT',
        status: string,
        weight: number,
        idx: number,
      ): Promise<Booking> =>
        bookingRepo.save(
          bookingRepo.create({
            reference,
            originYardId: direction === 'EXPORT' ? ethYard.id : djibYard.id,
            destinationYardId: direction === 'EXPORT' ? djibYard.id : ethYard.id,
            serviceTypeId: serviceType.id,
            status,
            paymentStatus: 'PAID',
            tradeDirection: direction,
            freightType: idx % 2 === 0 ? 'CONTAINER' : 'BULK',
            cargoTypeId: cargoType?.id ?? null,
            cargoFreeText: cargoType ? null : `${direction} demo cargo ${idx}`,
            cargoTotalWeightVgm: weight,
          }),
        );

      const makeInventory = async (
        booking: Booking,
        status: string,
        weight: number,
        extra: Partial<WarehouseInventory>,
      ): Promise<void> => {
        await invRepo.save(
          invRepo.create({
            warehouseId: warehouse.id,
            yardId: whYard.id,
            zoneId: whZone.id,
            bookingId: booking.id,
            quantity: 1,
            weight,
            status: status as WarehouseInventory['status'],
            notes: '[WH-DEMO]',
            ...extra,
          }),
        );
      };

      let created = 0;

      // 1) Export Receive Queue — 3 PAID export bookings, NO inventory.
      for (let i = 1; i <= 3; i++) {
        await makeBooking(`WH-DEMO-RCV-${i}`, 'EXPORT', 'PAID', 4000 + i * 500, i);
        created++;
      }

      // 2) Export Ready To Load — EXPORT inventory READY_FOR_LOADING + PASSED.
      for (let i = 1; i <= 3; i++) {
        const b = await makeBooking(`WH-DEMO-RTL-${i}`, 'EXPORT', 'PAID', 6000 + i * 500, i);
        await makeInventory(b, 'READY_FOR_LOADING', 6000 + i * 500, {
          inspectionStatus: 'PASSED',
          arrivedAt: ago(180),
          inspectedAt: ago(120),
          readyForLoadingAt: ago(60),
        });
        created++;
      }

      // 3) Export Loaded / Dispatch Queue — EXPORT inventory LOADED.
      for (let i = 1; i <= 2; i++) {
        const b = await makeBooking(`WH-DEMO-LOAD-${i}`, 'EXPORT', 'PAID', 7000 + i * 500, i);
        await makeInventory(b, 'LOADED', 7000 + i * 500, {
          inspectionStatus: 'PASSED',
          arrivedAt: ago(240),
          inspectedAt: ago(180),
          readyForLoadingAt: ago(120),
          loadedAt: ago(30),
        });
        created++;
      }

      // 4) Import Unloaded Queue — UNLOADED import inventory (not inspected, not stored).
      for (let i = 1; i <= 3; i++) {
        const b = await makeBooking(`WH-DEMO-UNL-${i}`, 'IMPORT', 'IN_TRANSIT', 5000 + i * 500, i);
        await makeInventory(b, 'UNLOADED', 5000 + i * 500, {
          arrivedAt: ago(90),
          unloadedAt: ago(45),
        });
        created++;
      }

      // 5) Import Dispatch Queue — READY_FOR_PICKUP import inventory (inspection PASSED).
      for (let i = 1; i <= 3; i++) {
        const b = await makeBooking(`WH-DEMO-PKR-${i}`, 'IMPORT', 'IN_TRANSIT', 5500 + i * 500, i);
        await makeInventory(b, 'READY_FOR_PICKUP', 5500 + i * 500, {
          inspectionStatus: 'PASSED',
          arrivedAt: ago(200),
          unloadedAt: ago(160),
          inspectedAt: ago(120),
          readyForPickupAt: ago(60),
        });
        created++;
      }

      // 6) Import Arrive Queue — an ARRIVED import train with IN_TRANSIT bookings, no inventory yet.
      await this.seedArrivedImportTrain(djibYard, ethYard, serviceType, cargoType, ago(60), ago(360));
      created += 1;

      this.logger.log(`✅ Warehouse demo seeded: ${created} buckets populated across every queue`);
    } catch (error) {
      this.logger.error(
        `WarehouseDemoSeeder failed: ${error instanceof Error ? error.message : String(error)}`,
      );
    }
  }

  /** An ARRIVED Djibouti→Ethiopia train with 3 IN_TRANSIT bookings (no inventory) for the Arrive Queue. */
  private async seedArrivedImportTrain(
    djibYard: Yard,
    ethYard: Yard,
    serviceType: ServiceType,
    cargoType: CargoType | null,
    arrival: Date,
    departure: Date,
  ): Promise<void> {
    const bookingRepo = this.dataSource.getRepository(Booking);
    const locoRepo = this.dataSource.getRepository(Locomotive);
    const trainSetRepo = this.dataSource.getRepository(TrainSet);
    const scheduleRepo = this.dataSource.getRepository(TrainSchedule);
    const scheduleBookingRepo = this.dataSource.getRepository(TrainScheduleBooking);

    const loco =
      (await locoRepo.findOne({ where: { code: 'WH-DEMO-LOCO' } })) ??
      (await locoRepo.save(locoRepo.create({ code: 'WH-DEMO-LOCO', name: 'Demo Locomotive', maxPullWeightTons: 4000 })));

    const trainSet = await trainSetRepo.save(
      trainSetRepo.create({
        locomotiveId: loco.id,
        totalWeightTons: 500,
        totalLengthMeters: 300,
        wagonCount: 10,
        status: 'COMPLETED',
      }),
    );

    const schedule = await scheduleRepo.save(
      scheduleRepo.create({
        trainSetId: trainSet.id,
        originStationId: djibYard.id,
        destinationStationId: ethYard.id,
        scheduledDepartureDate: departure,
        scheduledArrivalDate: arrival,
        actualArrivalAt: arrival,
        status: 'ARRIVED' as TrainSchedule['status'],
        trainNumber: 'WH-DEMO-IMP-TRAIN',
      }),
    );

    for (let i = 1; i <= 3; i++) {
      const b = await bookingRepo.save(
        bookingRepo.create({
          reference: `WH-DEMO-ARR-${i}`,
          originYardId: djibYard.id,
          destinationYardId: ethYard.id,
          serviceTypeId: serviceType.id,
          status: 'IN_TRANSIT',
          paymentStatus: 'PAID',
          tradeDirection: 'IMPORT',
          freightType: i % 2 === 0 ? 'CONTAINER' : 'BULK',
          cargoTypeId: cargoType?.id ?? null,
          cargoFreeText: cargoType ? null : `IMPORT arrive demo cargo ${i}`,
          cargoTotalWeightVgm: 5000 + i * 400,
        }),
      );
      await scheduleBookingRepo.save(
        scheduleBookingRepo.create({ trainScheduleId: schedule.id, bookingId: b.id }),
      );
    }
  }
}
