import 'reflect-metadata';
import { config } from 'dotenv';
import { resolve } from 'path';
import { WagonStatus } from '@edr/types';

config({ path: resolve(__dirname, '../../.env') });

import { AppDataSource } from '../data-source';
import { BookingContainer } from '../modules/bookings/entities/booking-container.entity';
import { Booking } from '../modules/bookings/entities/booking.entity';
import { Company, CompanyStatus, CompanyType } from '../modules/companies/entities/company.entity';
import { Locomotive } from '../modules/locomotives/entities/locomotive.entity';
import { ImportDjiboutiOperation } from '../modules/train-scheduling/entities/import-djibouti-operation.entity';
import { ContainerType } from '../modules/rule-engine/entities/container-type.entity';
import { ServiceType } from '../modules/rule-engine/entities/service-type.entity';
import { Yard } from '../modules/rule-engine/entities/yard.entity';
import { TrainScheduleBooking } from '../modules/train-schedules/entities/train-schedule-booking.entity';
import { WagonAllocationContainerItem } from '../modules/train-schedules/entities/wagon-allocation-container-item.entity';
import { WagonBookingAllocation } from '../modules/train-schedules/entities/wagon-booking-allocation.entity';
import { TrainSchedule } from '../modules/train-schedules/entities/train-schedule.entity';
import { TrainSetWagon } from '../modules/train-sets/entities/train-set-wagon.entity';
import { TrainSet } from '../modules/train-sets/entities/train-set.entity';
import { WagonType } from '../modules/wagon-types/entities/wagon-type.entity';
import { Wagon } from '../modules/wagons/entities/wagon.entity';

const TRAIN_NUMBER = 'NEGAD-INDODE-ARR-01';
const BOOKING_REFS = ['NEGAD-INDODE-BKG-001', 'NEGAD-INDODE-BKG-002', 'NEGAD-INDODE-BKG-003'] as const;

function addHours(date: Date, hours: number): Date {
  return new Date(date.getTime() + hours * 60 * 60 * 1000);
}

async function main() {
  const dataSource = await AppDataSource.initialize();

  try {
    await dataSource.transaction(async (manager) => {
      const yardRepo = manager.getRepository(Yard);
      const locomotiveRepo = manager.getRepository(Locomotive);
      const trainSetRepo = manager.getRepository(TrainSet);
      const scheduleRepo = manager.getRepository(TrainSchedule);
      const wagonTypeRepo = manager.getRepository(WagonType);
      const wagonRepo = manager.getRepository(Wagon);
      const trainSetWagonRepo = manager.getRepository(TrainSetWagon);
      const serviceTypeRepo = manager.getRepository(ServiceType);
      const containerTypeRepo = manager.getRepository(ContainerType);
      const companyRepo = manager.getRepository(Company);
      const bookingRepo = manager.getRepository(Booking);
      const bookingContainerRepo = manager.getRepository(BookingContainer);
      const scheduleBookingRepo = manager.getRepository(TrainScheduleBooking);
      const allocationRepo = manager.getRepository(WagonBookingAllocation);
      const containerItemRepo = manager.getRepository(WagonAllocationContainerItem);
      const importOperationRepo = manager.getRepository(ImportDjiboutiOperation);

      const negad =
        (await yardRepo.findOne({ where: { code: 'NEGAD' } })) ??
        (await yardRepo.save(
          yardRepo.create({
            code: 'NEGAD',
            label: 'Negad / Nagad',
            country: 'Djibouti',
            isActive: true,
            displayOrder: 5,
          }),
        ));
      if (negad.label !== 'Negad / Nagad') {
        negad.label = 'Negad / Nagad';
        await yardRepo.save(negad);
      }

      const indode =
        (await yardRepo.findOne({ where: { code: 'INDODE' } })) ??
        (await yardRepo.save(
          yardRepo.create({
            code: 'INDODE',
            label: 'Indode Dry Port',
            country: 'Ethiopia',
            isActive: true,
            displayOrder: 6,
          }),
        ));

      const locomotive =
        (await locomotiveRepo.findOne({ where: { code: 'NEGAD-INDODE-LOCO' } })) ??
        (await locomotiveRepo.save(
          locomotiveRepo.create({
            code: 'NEGAD-INDODE-LOCO',
            name: 'Negad to Indode Demo Locomotive',
            locomotiveType: 'DIESEL',
            maxPullWeightTons: 4200,
            maxTrainLengthMeters: 760,
            status: 'AVAILABLE',
            currentYardId: indode.id,
          }),
        ));

      const wagonType =
        (await wagonTypeRepo.findOne({ where: { code: 'NEGAD-FLAT' } })) ??
        (await wagonTypeRepo.save(
          wagonTypeRepo.create({
            code: 'NEGAD-FLAT',
            name: 'Negad Demo Flat Wagon',
            capacityTons: 70,
            lengthMeters: 14,
            supportedLoadTypes: ['CONTAINER'],
            isActive: true,
            equatedLengthM: 14,
            tareWeightTons: 20,
            supportsContainer: true,
            maxContainerGrossT: 70,
          }),
        ));

      const containerType =
        (await containerTypeRepo.findOne({ where: { code: '40FT' } })) ??
        (await containerTypeRepo.save(
          containerTypeRepo.create({
            code: '40FT',
            label: '40FT',
            sizeFt: 40,
            isReefer: false,
            isOpenTop: false,
            isActive: true,
            displayOrder: 2,
          }),
        ));

      const serviceType =
        (await serviceTypeRepo.findOne({ where: { code: 'RAIL_CONTAINER' } })) ??
        (await serviceTypeRepo.save(
          serviceTypeRepo.create({
            code: 'RAIL_CONTAINER',
            serviceName: 'Rail Container Service',
            description: 'Rail container service for demo marshalling',
            canBeBookedAlone: true,
            includesFirstMile: false,
            includesLastMile: false,
            includesCustoms: false,
            isActive: true,
            displayOrder: 1,
          }),
        ));

      const company =
        (await companyRepo.findOne({ where: { tin: 'NEGADIND01' } })) ??
        (await companyRepo.save(
          companyRepo.create({
            name: 'Negad Indode Marshalling Demo Customer',
            type: CompanyType.Customer,
            status: CompanyStatus.Active,
            tin: 'NEGADIND01',
            vatNumber: 'NEGADIND01',
            fanNumber: 'NEGADINDODE00001',
            country: 'Ethiopia',
            address: 'Indode Dry Port',
            phone: '251900000202',
            email: 'negad-indode-demo@edr.local',
            contactPersonName: 'Marshalling Demo',
            contactPersonPhone: '251900000202',
            generalManagerName: 'Demo Manager',
            generalManagerEmail: 'negad-indode-demo@edr.local',
            generalManagerPhone: '251900000202',
          }),
        ));

      const now = new Date();
      const departure = addHours(now, -12);
      const arrival = now;

      let schedule = await scheduleRepo.findOne({ where: { trainNumber: TRAIN_NUMBER } });
      if (!schedule) {
        const trainSet = await trainSetRepo.save(
          trainSetRepo.create({
            locomotiveId: locomotive.id,
            totalWeightTons: 960,
            totalLengthMeters: 420,
            wagonCount: BOOKING_REFS.length,
            status: 'COMPLETED',
          }),
        );

        schedule = scheduleRepo.create({
          trainSetId: trainSet.id,
          originStationId: negad.id,
          destinationStationId: indode.id,
          scheduledDepartureDate: departure,
          scheduledArrivalDate: arrival,
          actualDepartureAt: departure,
          actualArrivalAt: arrival,
          status: 'ARRIVED' as TrainSchedule['status'],
          trainNumber: TRAIN_NUMBER,
          direction: 'IMPORT',
          maxWagons: 53,
          bookingWindowStatus: 'CLOSED',
        });
      } else {
        schedule.originStationId = negad.id;
        schedule.destinationStationId = indode.id;
        schedule.scheduledDepartureDate = departure;
        schedule.scheduledArrivalDate = arrival;
        schedule.actualDepartureAt = departure;
        schedule.actualArrivalAt = arrival;
        schedule.status = 'ARRIVED' as TrainSchedule['status'];
        schedule.direction = 'IMPORT';
        schedule.bookingWindowStatus = 'CLOSED';

        if (schedule.trainSetId) {
          await trainSetRepo.update(schedule.trainSetId, { status: 'COMPLETED' });
        }
      }

      const saved = await scheduleRepo.save(schedule);
      await trainSetRepo.update(saved.trainSetId, {
        totalWeightTons: BOOKING_REFS.length * 28,
        totalLengthMeters: BOOKING_REFS.length * 14,
        wagonCount: BOOKING_REFS.length,
        status: 'COMPLETED',
      });

      const existingSlots = await trainSetWagonRepo.find({ where: { trainSetId: saved.trainSetId } });
      const existingAllocations = existingSlots.length
        ? await allocationRepo.find({
            where: existingSlots.map((slot) => ({ trainSetWagonId: slot.id })),
          })
        : [];
      if (existingAllocations.length) {
        await containerItemRepo.delete(
          existingAllocations.map((allocation) => ({ wagonBookingAllocationId: allocation.id })),
        );
      }
      if (existingSlots.length) {
        await allocationRepo.delete(existingSlots.map((slot) => ({ trainSetWagonId: slot.id })));
        await wagonRepo.update(
          existingSlots.map((slot) => ({ trainSetWagonId: slot.id })),
          {
            trainSetWagonId: null,
            currentTrainScheduleId: null,
            sequenceNumber: null,
            status: WagonStatus.Available,
          },
        );
        await trainSetWagonRepo.delete({ trainSetId: saved.trainSetId });
      }

      for (const [index, reference] of BOOKING_REFS.entries()) {
        const sequenceNo = index + 1;
        const containerNumber = `NEGADIND${String(sequenceNo).padStart(4, '0')}`;
        const weightTons = 26 + sequenceNo;

        let booking = await bookingRepo.findOne({ where: { reference } });
        if (!booking) {
          booking = bookingRepo.create({ reference });
        }
        Object.assign(booking, {
          companyId: company.id,
          originYardId: negad.id,
          destinationYardId: indode.id,
          serviceTypeId: serviceType.id,
          status: 'IN_TRANSIT',
          paymentStatus: 'PAID',
          scheduledDate: departure,
          estimatedShipmentDate: departure,
          contractType: 'SPOT',
          equipmentReturn: 'TERMINAL',
          paymentCurrency: 'ETB',
          totalAmount: 0,
          isGovernment: false,
          tradeDirection: 'IMPORT',
          freightType: 'CONTAINER',
          cargoTypeId: null,
          cargoFreeText: `Negad to Indode demo container ${sequenceNo}`,
          cargoTotalWeightVgm: weightTons,
          priorityScore: 75 - index,
          trainScheduleId: saved.id,
          schedulingStatus: 'SCHEDULED',
          scheduledAt: now,
          wagonsRequired: 1,
        });
        booking = await bookingRepo.save(booking);

        await bookingContainerRepo.delete({ bookingId: booking.id });
        const bookingContainer = await bookingContainerRepo.save(
          bookingContainerRepo.create({
            bookingId: booking.id,
            containerTypeId: containerType.id,
            containerNumber,
            quantity: 1,
            vgmPerUnitTons: weightTons,
            totalVgmTons: weightTons,
            wagonsRequired: 1,
            weightLimitRuleId: null,
            isOverweight: false,
            overweightExcessTons: null,
          }),
        );

        await scheduleBookingRepo.upsert(
          { trainScheduleId: saved.id, bookingId: booking.id },
          { conflictPaths: { trainScheduleId: true, bookingId: true } },
        );

        const wagon = await wagonRepo.save(
          wagonRepo.create({
            wagonNumber: `NEGAD-INDODE-WGN-${String(sequenceNo).padStart(2, '0')}`,
            wagonTypeId: wagonType.id,
            trainId: null,
            sequenceNumber: sequenceNo,
            status: WagonStatus.Assigned,
            currentYardId: indode.id,
            notes: 'Demo wagon for Negad to Indode marshalling',
            trainSetWagonId: null,
            currentTrainScheduleId: saved.id,
          }),
        );

        const trainSetWagon = await trainSetWagonRepo.save(
          trainSetWagonRepo.create({
            trainSetId: saved.trainSetId,
            wagonTypeId: wagonType.id,
            physicalWagonId: wagon.id,
            sequenceNo,
            capacityTons: 70,
            lengthMeters: 14,
            assignedWeightTons: weightTons,
            status: 'LOADED',
          }),
        );
        await wagonRepo.update(wagon.id, { trainSetWagonId: trainSetWagon.id });

        const allocation = await allocationRepo.save(
          allocationRepo.create({
            trainSetWagonId: trainSetWagon.id,
            bookingId: booking.id,
            allocatedWeightTons: weightTons,
            loadType: 'CONTAINER',
            status: 'LOADED',
            confirmedAt: now,
          }),
        );

        await containerItemRepo.save(
          containerItemRepo.create({
            wagonBookingAllocationId: allocation.id,
            bookingContainerId: bookingContainer.id,
            containerId: null,
            containerNumber,
            containerTypeId: containerType.id,
            positionOnWagon: 1,
            sealNumber: `SEAL-${containerNumber}`,
            grossWeightTons: weightTons,
          }),
        );
      }

      await importOperationRepo.upsert(
        {
          trainScheduleId: saved.id,
          documents: {},
          gatepassGrantedAt: departure,
          readyForLoadingAt: departure,
          loadedOnTrainAt: departure,
          departedFromDjiboutiAt: departure,
          loadListGeneratedAt: now,
          performedBy: 'Seed Demo',
          notes: 'Seeded marshalling data for Negad to Indode arrived train',
        },
        { conflictPaths: { trainScheduleId: true } },
      );

      console.log(`Seeded ARRIVED train ${TRAIN_NUMBER}`);
      console.log(`Schedule ID: ${saved.id}`);
      console.log(`Route: ${negad.code} -> ${indode.code}`);
      console.log(`Marshalling data: ${BOOKING_REFS.length} bookings, wagons and allocations`);
    });
  } finally {
    await dataSource.destroy();
  }
}

main().catch((err) => {
  console.error('Negad to Indode arrived train seed failed:', err);
  process.exit(1);
});
