import { Injectable, Logger } from '@nestjs/common';
import { randomUUID } from 'crypto';
import { DataSource, In } 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 { FirstMile } from '../modules/first-mile/entities/first-mile.entity';
import { LastMile } from '../modules/last-mile/entities/last-mile.entity';
import { wagonsPerUnitForSize } from '../modules/rule-engine/container-type.util';
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';

const SERVICE_TYPE_CODE = 'RAIL_CONTAINER_FIRST_LAST';
const COMPANY_TIN = 'FLMDEMO001';
const COMPANY_EMAIL = 'first-last-mile-demo@edr.local';

const YARDS = [
  { code: 'DJIBOUTI', label: 'Djibouti', country: 'Djibouti' as const, displayOrder: 1 },
  { code: 'ADDIS_ABABA', label: 'Addis Ababa', country: 'Ethiopia' as const, displayOrder: 2 },
];

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

const DEMO_BOOKINGS = [
  {
    reference: 'DEMO-IMP-FLM-001',
    tradeDirection: 'IMPORT',
    containerCode: '40FT',
    quantity: 8,
    totalWeightTons: 224,
    originCode: 'DJIBOUTI',
    destinationCode: 'ADDIS_ABABA',
    scheduledDate: '2026-07-01T08:00:00.000Z',
    firstMilePickupAddress: 'Doraleh Container Terminal, Djibouti',
    firstMilePickupLat: 11.5881,
    firstMilePickupLng: 43.1372,
    lastMileDeliveryAddress: 'Akaki Industrial Zone, Addis Ababa',
    lastMileDeliveryLat: 8.8808,
    lastMileDeliveryLng: 38.7876,
  },
  {
    reference: 'DEMO-IMP-FLM-002',
    tradeDirection: 'IMPORT',
    containerCode: '20FT',
    quantity: 12,
    totalWeightTons: 240,
    originCode: 'DJIBOUTI',
    destinationCode: 'ADDIS_ABABA',
    scheduledDate: '2026-07-02T08:00:00.000Z',
    firstMilePickupAddress: 'PK12 Dry Port, Djibouti',
    firstMilePickupLat: 11.5536,
    firstMilePickupLng: 43.1103,
    lastMileDeliveryAddress: 'Kality Logistics Hub, Addis Ababa',
    lastMileDeliveryLat: 8.9137,
    lastMileDeliveryLng: 38.7815,
  },
  {
    reference: 'DEMO-IMP-FLM-003',
    tradeDirection: 'IMPORT',
    containerCode: '40FT',
    quantity: 6,
    totalWeightTons: 180,
    originCode: 'DJIBOUTI',
    destinationCode: 'ADDIS_ABABA',
    scheduledDate: '2026-07-03T08:00:00.000Z',
    firstMilePickupAddress: 'Djibouti Free Zone Yard',
    firstMilePickupLat: 11.5947,
    firstMilePickupLng: 43.1471,
    lastMileDeliveryAddress: 'Bole Lemi Industrial Park, Addis Ababa',
    lastMileDeliveryLat: 8.9806,
    lastMileDeliveryLng: 38.8736,
  },
  {
    reference: 'DEMO-IMP-FLM-004',
    tradeDirection: 'IMPORT',
    containerCode: '20FT',
    quantity: 10,
    totalWeightTons: 210,
    originCode: 'DJIBOUTI',
    destinationCode: 'ADDIS_ABABA',
    scheduledDate: '2026-07-04T08:00:00.000Z',
    firstMilePickupAddress: 'Doraleh Multipurpose Port, Djibouti',
    firstMilePickupLat: 11.6062,
    firstMilePickupLng: 43.1219,
    lastMileDeliveryAddress: 'Addis Ababa Freight Terminal',
    lastMileDeliveryLat: 9.0101,
    lastMileDeliveryLng: 38.7619,
  },
  {
    reference: 'DEMO-IMP-FLM-005',
    tradeDirection: 'IMPORT',
    containerCode: '40FT',
    quantity: 5,
    totalWeightTons: 150,
    originCode: 'DJIBOUTI',
    destinationCode: 'ADDIS_ABABA',
    scheduledDate: '2026-07-05T08:00:00.000Z',
    firstMilePickupAddress: 'Djibouti Port Gate 3',
    firstMilePickupLat: 11.5999,
    firstMilePickupLng: 43.1344,
    lastMileDeliveryAddress: 'Sebeta Distribution Center',
    lastMileDeliveryLat: 8.9169,
    lastMileDeliveryLng: 38.6177,
  },
  {
    reference: 'DEMO-EXP-FLM-001',
    tradeDirection: 'EXPORT',
    containerCode: '40FT',
    quantity: 7,
    totalWeightTons: 196,
    originCode: 'ADDIS_ABABA',
    destinationCode: 'DJIBOUTI',
    scheduledDate: '2026-07-01T10:00:00.000Z',
    firstMilePickupAddress: 'Bole Lemi Industrial Park, Addis Ababa',
    firstMilePickupLat: 8.9806,
    firstMilePickupLng: 38.8736,
    lastMileDeliveryAddress: 'Doraleh Container Terminal, Djibouti',
    lastMileDeliveryLat: 11.5881,
    lastMileDeliveryLng: 43.1372,
  },
  {
    reference: 'DEMO-EXP-FLM-002',
    tradeDirection: 'EXPORT',
    containerCode: '20FT',
    quantity: 11,
    totalWeightTons: 220,
    originCode: 'ADDIS_ABABA',
    destinationCode: 'DJIBOUTI',
    scheduledDate: '2026-07-02T10:00:00.000Z',
    firstMilePickupAddress: 'Akaki Industrial Zone, Addis Ababa',
    firstMilePickupLat: 8.8808,
    firstMilePickupLng: 38.7876,
    lastMileDeliveryAddress: 'Djibouti Free Zone Yard',
    lastMileDeliveryLat: 11.5947,
    lastMileDeliveryLng: 43.1471,
  },
  {
    reference: 'DEMO-EXP-FLM-003',
    tradeDirection: 'EXPORT',
    containerCode: '40FT',
    quantity: 4,
    totalWeightTons: 128,
    originCode: 'ADDIS_ABABA',
    destinationCode: 'DJIBOUTI',
    scheduledDate: '2026-07-03T10:00:00.000Z',
    firstMilePickupAddress: 'Kality Logistics Hub, Addis Ababa',
    firstMilePickupLat: 8.9137,
    firstMilePickupLng: 38.7815,
    lastMileDeliveryAddress: 'Doraleh Multipurpose Port, Djibouti',
    lastMileDeliveryLat: 11.6062,
    lastMileDeliveryLng: 43.1219,
  },
  {
    reference: 'DEMO-EXP-FLM-004',
    tradeDirection: 'EXPORT',
    containerCode: '20FT',
    quantity: 9,
    totalWeightTons: 180,
    originCode: 'ADDIS_ABABA',
    destinationCode: 'DJIBOUTI',
    scheduledDate: '2026-07-04T10:00:00.000Z',
    firstMilePickupAddress: 'Addis Ababa Freight Terminal',
    firstMilePickupLat: 9.0101,
    firstMilePickupLng: 38.7619,
    lastMileDeliveryAddress: 'PK12 Dry Port, Djibouti',
    lastMileDeliveryLat: 11.5536,
    lastMileDeliveryLng: 43.1103,
  },
  {
    reference: 'DEMO-EXP-FLM-005',
    tradeDirection: 'EXPORT',
    containerCode: '40FT',
    quantity: 6,
    totalWeightTons: 168,
    originCode: 'ADDIS_ABABA',
    destinationCode: 'DJIBOUTI',
    scheduledDate: '2026-07-05T10:00:00.000Z',
    firstMilePickupAddress: 'Sebeta Distribution Center',
    firstMilePickupLat: 8.9169,
    firstMilePickupLng: 38.6177,
    lastMileDeliveryAddress: 'Djibouti Port Gate 3',
    lastMileDeliveryLat: 11.5999,
    lastMileDeliveryLng: 43.1344,
  },
] as const;

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

  constructor(private readonly dataSource: DataSource) {}

  async run() {
    await this.dataSource.transaction(async (manager) => {
      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 with First and Last Mile',
          description: 'Demo service type for approved first/last-mile bookings',
          canBeBookedAlone: true,
          includesFirstMile: true,
          includesLastMile: true,
          includesCustoms: false,
          isActive: true,
          displayOrder: 10,
        },
        { conflictPaths: { code: true } },
      );

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

      await manager.getRepository(Company).upsert(
        {
          name: 'First and Last Mile Demo Customer',
          type: CompanyType.Customer,
          status: CompanyStatus.Active,
          tin: COMPANY_TIN,
          vatNumber: COMPANY_TIN,
          fanNumber: 'FLM0000000000001',
          country: 'Ethiopia',
          address: 'Addis Ababa',
          phone: '251900000101',
          email: COMPANY_EMAIL,
          website: null,
          contactPersonName: 'First Last Mile Demo',
          contactPersonPhone: '251900000101',
          generalManagerName: 'Demo Manager',
          generalManagerEmail: COMPANY_EMAIL,
          generalManagerPhone: '251900000101',
        },
        { 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(`approved_first_last_mile_demo_dependency_missing:${demoBooking.reference}`);
        }

        const wagonsRequired =
          Number(demoBooking.quantity) * wagonsPerUnitForSize(containerType.sizeFt);
        const vgmPerUnitTons = demoBooking.totalWeightTons / demoBooking.quantity;

        await manager.getRepository(Booking).upsert(
          {
            reference: demoBooking.reference,
            companyId: company.id,
            status: 'APPROVED',
            scheduledDate: new Date(demoBooking.scheduledDate),
            estimatedShipmentDate: new Date(demoBooking.scheduledDate),
            totalAmount: demoBooking.totalWeightTons * 25,
            paymentStatus: 'PENDING',
            contractType: 'NEW',
            serviceTypeId: serviceType.id,
            firstMilePickupAddress: demoBooking.firstMilePickupAddress,
            firstMilePickupLat: demoBooking.firstMilePickupLat,
            firstMilePickupLng: demoBooking.firstMilePickupLng,
            lastMileDeliveryAddress: demoBooking.lastMileDeliveryAddress,
            lastMileDeliveryLat: demoBooking.lastMileDeliveryLat,
            lastMileDeliveryLng: demoBooking.lastMileDeliveryLng,
            equipmentReturn: 'WITHOUT_RETURN',
            originYardId: origin.id,
            destinationYardId: destination.id,
            tradeDirection: demoBooking.tradeDirection,
            freightType: 'CONTAINER',
            cargoTypeId: null,
            cargoFreeText: 'Demo container cargo',
            shippingLineId: null,
            cargoTotalWeightVgm: demoBooking.totalWeightTons,
            isHazardous: false,
            isReefer: false,
            paymentCurrency: 'ETB',
            approvedByStaffAt: new Date(),
            priorityScore: 20,
            wagonsRequired,
            schedulingStatus: 'NOT_SCHEDULED',
            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,
          weightLimitRuleId: null,
          isOverweight: vgmPerUnitTons > 35,
          overweightExcessTons: vgmPerUnitTons > 35 ? vgmPerUnitTons - 35 : null,
        });
      }

      const bookings = await manager.getRepository(Booking).find({
        where: { reference: In(DEMO_BOOKINGS.map((booking) => booking.reference)) },
        select: { id: true },
      });
      const bookingIds = bookings.map((booking) => booking.id);

      await manager.getRepository(FirstMile).delete({ bookingId: In(bookingIds) });
      await manager.getRepository(LastMile).delete({ bookingId: In(bookingIds) });

      await manager.getRepository(FirstMile).insert(
        bookingIds.map((bookingId) => ({
          bookingId,
          status: 'READY_TO_TRANSIT',
          advancedPayment: 0,
          remainingPayment: 0,
          estimatedKm: 18,
          exactKm: null,
          vehicleId: null,
        })),
      );

      await manager.getRepository(LastMile).insert(
        bookingIds.map((bookingId) => ({
          bookingId,
          status: 'READY_TO_TRANSIT',
          advancedPayment: 0,
          remainingPayment: 0,
          estimatedKm: 22,
          exactKm: null,
          vehicleId: null,
        })),
      );
    });

    this.logger.log('Seeded 5 import and 5 export approved bookings with first/last-mile legs');
  }
}
