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 { 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_PAID_MILE';
const COMPANY_TIN = 'PAIDMILE001';
const COMPANY_EMAIL = 'paid-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 },
];

/**
 * Six paid, approved container bookings that mirror the real trucking legs:
 *  - EXPORT (Ethiopia -> Djibouti) carries a FIRST-MILE leg (factory -> rail terminal).
 *  - IMPORT (Djibouti -> Ethiopia) carries a LAST-MILE leg (dry port -> final delivery).
 * Each booking is paymentStatus PAID and its single mile leg is marked paid + ready to transit.
 */
const DEMO_BOOKINGS = [
  // ── IMPORT: last mile only ─────────────────────────────────────────────
  {
    reference: 'PAID-IMP-001',
    tradeDirection: 'IMPORT',
    containerCode: '40FT',
    quantity: 8,
    totalWeightTons: 224,
    originCode: 'DJIBOUTI',
    destinationCode: 'ADDIS_ABABA',
    scheduledDate: '2026-07-01T08:00:00.000Z',
    lastMileDeliveryAddress: 'Akaki Industrial Zone, Addis Ababa',
    lastMileDeliveryLat: 8.8808,
    lastMileDeliveryLng: 38.7876,
  },
  {
    reference: 'PAID-IMP-002',
    tradeDirection: 'IMPORT',
    containerCode: '20FT',
    quantity: 12,
    totalWeightTons: 240,
    originCode: 'DJIBOUTI',
    destinationCode: 'ADDIS_ABABA',
    scheduledDate: '2026-07-02T08:00:00.000Z',
    lastMileDeliveryAddress: 'Kality Logistics Hub, Addis Ababa',
    lastMileDeliveryLat: 8.9137,
    lastMileDeliveryLng: 38.7815,
  },
  {
    reference: 'PAID-IMP-003',
    tradeDirection: 'IMPORT',
    containerCode: '40FT',
    quantity: 6,
    totalWeightTons: 180,
    originCode: 'DJIBOUTI',
    destinationCode: 'ADDIS_ABABA',
    scheduledDate: '2026-07-03T08:00:00.000Z',
    lastMileDeliveryAddress: 'Bole Lemi Industrial Park, Addis Ababa',
    lastMileDeliveryLat: 8.9806,
    lastMileDeliveryLng: 38.8736,
  },
  // ── EXPORT: first mile only ────────────────────────────────────────────
  {
    reference: 'PAID-EXP-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,
  },
  {
    reference: 'PAID-EXP-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,
  },
  {
    reference: 'PAID-EXP-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,
  },
] as const;

@Injectable()
export class PaidImportExportMileDemoSeeder {
  private readonly logger = new Logger(PaidImportExportMileDemoSeeder.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 Paid First/Last Mile',
          description: 'Demo service type for paid import/export bookings with a single mile leg',
          canBeBookedAlone: true,
          includesFirstMile: true,
          includesLastMile: true,
          includesCustoms: false,
          isActive: true,
          displayOrder: 11,
        },
        { 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: 'Paid Import/Export Mile Demo Customer',
          type: CompanyType.Customer,
          status: CompanyStatus.Active,
          tin: COMPANY_TIN,
          vatNumber: COMPANY_TIN,
          fanNumber: 'PMD0000000000001',
          country: 'Ethiopia',
          address: 'Addis Ababa',
          phone: '251900000202',
          email: COMPANY_EMAIL,
          website: null,
          contactPersonName: 'Paid Mile Demo',
          contactPersonPhone: '251900000202',
          generalManagerName: 'Demo Manager',
          generalManagerEmail: COMPANY_EMAIL,
          generalManagerPhone: '251900000202',
        },
        { 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(`paid_import_export_mile_demo_dependency_missing:${demoBooking.reference}`);
        }

        const isImport = demoBooking.tradeDirection === 'IMPORT';
        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: 'PAID',
            contractType: 'NEW',
            serviceTypeId: serviceType.id,
            // Only the leg that matches the trade direction carries an address.
            firstMilePickupAddress: isImport ? null : demoBooking.firstMilePickupAddress,
            firstMilePickupLat: isImport ? null : demoBooking.firstMilePickupLat,
            firstMilePickupLng: isImport ? null : demoBooking.firstMilePickupLng,
            lastMileDeliveryAddress: isImport ? demoBooking.lastMileDeliveryAddress : null,
            lastMileDeliveryLat: isImport ? demoBooking.lastMileDeliveryLat : null,
            lastMileDeliveryLng: isImport ? demoBooking.lastMileDeliveryLng : null,
            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,
        });

        // Reset any existing legs for idempotency, then create the single paid leg.
        await manager.getRepository(FirstMile).delete({ bookingId: booking.id });
        await manager.getRepository(LastMile).delete({ bookingId: booking.id });

        const paidAmount = demoBooking.totalWeightTons * 25;

        if (isImport) {
          await manager.getRepository(LastMile).insert({
            bookingId: booking.id,
            status: 'READY_TO_TRANSIT',
            advancedPayment: paidAmount,
            remainingPayment: 0,
            paid: true,
            estimatedKm: 22,
            exactKm: null,
            vehicleId: null,
          });
        } else {
          await manager.getRepository(FirstMile).insert({
            bookingId: booking.id,
            status: 'READY_TO_TRANSIT',
            advancedPayment: paidAmount,
            remainingPayment: 0,
            paid: true,
            estimatedKm: 18,
            exactKm: null,
            vehicleId: null,
          });
        }
      }
    });

    this.logger.log(
      'Seeded 6 paid bookings: 3 import (last-mile) + 3 export (first-mile).',
    );
  }
}
