import { Injectable, Logger } from "@nestjs/common";
import { randomUUID } from "crypto";
import { DataSource } from "typeorm";

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 { ServiceType } from "../modules/rule-engine/entities/service-type.entity";
import { Yard } from "../modules/rule-engine/entities/yard.entity";
import { WagonType } from "../modules/wagon-types/entities/wagon-type.entity";
import { ContainerType } from "../modules/rule-engine/entities/container-type.entity";

const SEED_FLAG = "SEED_DEMO_BOOKINGS";

const SERVICE_TYPE_CODE = "RAIL_CONTAINER";
const COMPANY_EMAIL = "train-scheduling-demo@edr.local";
const COMPANY_TIN = "1234567890";

const YARDS = [
  { code: "DJIBOUTI", label: "Djibouti", country: "Djibouti", displayOrder: 1 },
  {
    code: "ADDIS_ABABA",
    label: "Addis Ababa",
    country: "Ethiopia",
    displayOrder: 2,
  },
  {
    code: "DIRE_DAWA",
    label: "Dire Dawa",
    country: "Ethiopia",
    displayOrder: 3,
  },
];

const CONTAINER_TYPES = [
  { code: "20FT", label: "20FT", sizeFt: 20 },
  { code: "40FT", label: "40FT", sizeFt: 40 },
];

const DEMO_BOOKINGS = [
  {
    reference: "BKG-CONT-001",
    containerCode: "40FT",
    quantity: 20,
    totalWeightTons: 500,
    originCode: "DJIBOUTI",
    destinationCode: "ADDIS_ABABA",
    scheduledDate: "2026-06-20T08:00:00.000Z",
    status: "PAID",
    paymentStatus: "PAID",
  },
  {
    reference: "BKG-CONT-002",
    containerCode: "20FT",
    quantity: 10,
    totalWeightTons: 300,
    originCode: "DJIBOUTI",
    destinationCode: "ADDIS_ABABA",
    scheduledDate: "2026-06-20T08:00:00.000Z",
    status: "PAID",
    paymentStatus: "PAID",
  },
  {
    reference: "BKG-CONT-003",
    containerCode: "40FT",
    quantity: 15,
    totalWeightTons: 450,
    originCode: "DJIBOUTI",
    destinationCode: "ADDIS_ABABA",
    scheduledDate: "2026-06-20T08:00:00.000Z",
    status: "PAID",
    paymentStatus: "PAID",
  },
  {
    reference: "BKG-CONT-007",
    containerCode: "20FT",
    quantity: 6,
    totalWeightTons: 180,
    originCode: "DJIBOUTI",
    destinationCode: "ADDIS_ABABA",
    scheduledDate: "2026-06-20T08:00:00.000Z",
    status: "PAID",
    paymentStatus: "PAID",
  },
  {
    reference: "BKG-CONT-008",
    containerCode: "40FT",
    quantity: 4,
    totalWeightTons: 120,
    originCode: "DJIBOUTI",
    destinationCode: "ADDIS_ABABA",
    scheduledDate: "2026-06-20T08:00:00.000Z",
    status: "PAID",
    paymentStatus: "PAID",
  },
  {
    reference: "BKG-CONT-009",
    containerCode: "20FT",
    quantity: 5,
    totalWeightTons: 110,
    originCode: "DJIBOUTI",
    destinationCode: "ADDIS_ABABA",
    scheduledDate: "2026-06-20T08:00:00.000Z",
    status: "PAID",
    paymentStatus: "PAID",
  },
  {
    reference: "BKG-CONT-004",
    containerCode: "40FT",
    quantity: 12,
    totalWeightTons: 360,
    originCode: "ADDIS_ABABA",
    destinationCode: "DIRE_DAWA",
    scheduledDate: "2026-06-20T08:00:00.000Z",
    status: "PAID",
    paymentStatus: "PAID",
  },
  {
    reference: "BKG-CONT-005",
    containerCode: "20FT",
    quantity: 8,
    totalWeightTons: 160,
    originCode: "DJIBOUTI",
    destinationCode: "ADDIS_ABABA",
    scheduledDate: "2026-06-21T08:00:00.000Z",
    status: "PAID",
    paymentStatus: "PAID",
  },
  {
    reference: "BKG-CONT-006",
    containerCode: "40FT",
    quantity: 80,
    totalWeightTons: 3600,
    originCode: "DJIBOUTI",
    destinationCode: "ADDIS_ABABA",
    scheduledDate: "2026-06-20T08:00:00.000Z",
    status: "PAID",
    paymentStatus: "PAID",
  },
];

@Injectable()
export class DemoBookingsSeeder {
  private readonly logger = new Logger(DemoBookingsSeeder.name);

  constructor(private readonly dataSource: DataSource) { }

  async run() {
    const shouldSeed = process.env[SEED_FLAG]?.trim().toLowerCase() === "true";
    if (!shouldSeed) {
      this.logger.log(
        `Skipping demo booking seed because ${SEED_FLAG} is not enabled`,
      );
      return;
    }

    await this.dataSource.transaction(async (manager) => {
      await manager.getRepository(WagonType).upsert(
        {
          code: "NW5",
          name: "Flat Wagon",
          capacityTons: 70,
          lengthMeters: 14,
          maxWagonsPerTrain: 53,
          supportedLoadTypes: ["CONTAINER"],
          isActive: true,
        },
        { conflictPaths: { code: true } },
      );

      await manager.getRepository(Locomotive).upsert(
        [
          {
            code: "LOC-001",
            name: "Demo Locomotive 1",
            locomotiveType: 'ELECTRIC',
            maxPullWeightTons: 3500,
            maxTrainLengthMeters: 760,
            status: "AVAILABLE",
          },
          {
            code: "LOC-002",
            name: "Demo Locomotive 2",
            locomotiveType: 'DIESEL',
            maxPullWeightTons: 2500,
            maxTrainLengthMeters: 760,
            status: "AVAILABLE",
          },
        ],
        { conflictPaths: { code: true } },
      );

      await manager.getRepository(Yard).upsert(
        YARDS.map((yard) => ({ ...yard, isActive: true })),
        { conflictPaths: { code: true } },
      );

      await manager.getRepository(ServiceType).upsert(
        {
          code: SERVICE_TYPE_CODE,
          serviceName: "Rail Container Service",
          description: "Temporary service type for train scheduling demos",
          canBeBookedAlone: true,
          includesFirstMile: false,
          includesLastMile: false,
          includesCustoms: false,
          priorityBonusPoints: 0,
          isActive: true,
          displayOrder: 1,
        },
        { conflictPaths: { code: true } },
      );

      await manager.getRepository(ContainerType).upsert(
        CONTAINER_TYPES.map((containerType, index) => ({
          ...containerType,
          wagonsPerUnit: 1,
          isReefer: false,
          isOpenTop: false,
          isActive: true,
          displayOrder: index + 1,
        })),
        { conflictPaths: { code: true } },
      );

      await manager.getRepository(Company).upsert(
        {
          name: "Train Scheduling Demo Customer",
          type: CompanyType.Customer,
          status: CompanyStatus.Active,
          tin: COMPANY_TIN,
          vatNumber: "1234567890",
          fanNumber: "1234567890123456",
          country: "Ethiopia",
          address: "Demo Address",
          phone: "251900000001",
          email: COMPANY_EMAIL,
          website: null,
          contactPersonName: "Train Scheduling",
          contactPersonPhone: "251900000001",
          generalManagerName: "Demo Manager",
          generalManagerEmail: COMPANY_EMAIL,
          generalManagerPhone: "251900000001",
        },
        { conflictPaths: { tin: true } },
      );

      const [serviceType, company, yards, containerTypes] = await Promise.all([
        manager
          .getRepository(ServiceType)
          .findOneByOrFail({ code: SERVICE_TYPE_CODE }),
        manager
          .getRepository(Company)
          .findOneByOrFail({ tin: COMPANY_TIN }),
        manager.getRepository(Yard).find(),
        manager.getRepository(ContainerType).find(),
      ]);

      const yardByCode = new Map(yards.map((yard) => [yard.code, yard]));
      const containerTypeByCode = new Map(
        containerTypes.map((containerType) => [
          containerType.code,
          containerType,
        ]),
      );

      for (const demoBooking of DEMO_BOOKINGS) {
        const origin = yardByCode.get(demoBooking.originCode);
        const destination = yardByCode.get(demoBooking.destinationCode);
        const containerType = containerTypeByCode.get(
          demoBooking.containerCode,
        );

        if (!origin || !destination || !containerType) {
          throw new Error(
            `demo_booking_seed_dependency_missing:${demoBooking.reference}`,
          );
        }

        const vgmPerUnitTons =
          demoBooking.totalWeightTons / demoBooking.quantity;

        await manager.getRepository(Booking).upsert(
          {
            reference: demoBooking.reference,
            companyId: company.id,
            status: demoBooking.status,
            scheduledDate: new Date(demoBooking.scheduledDate),
            totalAmount: 0,
            paymentStatus: demoBooking.paymentStatus,
            contractType: "NEW",
            serviceTypeId: serviceType.id,
            equipmentReturn: "WITHOUT_RETURN",
            originYardId: origin.id,
            destinationYardId: destination.id,
            tradeDirection: "IMPORT",
            freightType: "CONTAINER",
            cargoTypeId: null,
            cargoFreeText: null,
            shippingLineId: null,
            cargoTotalWeightVgm: demoBooking.totalWeightTons,
            isHazardous: false,
            paymentCurrency: "USD",
            allowConsolidation: false,
            priorityScore: 0,
            versionNumber: 1,
          },
          { conflictPaths: { reference: true } },
        );

        const booking = await manager.getRepository(Booking).findOneByOrFail({
          reference: demoBooking.reference,
        });

        await manager
          .getRepository(BookingContainer)
          .delete({ bookingId: booking.id });
        await manager.getRepository(BookingContainer).insert({
          id: randomUUID(),
          bookingId: booking.id,
          containerTypeId: containerType.id,
          quantity: demoBooking.quantity,
          vgmPerUnitTons,
          totalVgmTons: demoBooking.totalWeightTons,
          wagonsRequired: Math.ceil(demoBooking.totalWeightTons / 70),
          weightLimitRuleId: null,
          isOverweight: demoBooking.totalWeightTons > 70,
          overweightExcessTons:
            demoBooking.totalWeightTons > 70
              ? demoBooking.totalWeightTons - 70
              : null,
        });
      }
    });

    this.logger.log("Seeded demo train scheduling data");
  }
}
