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

import { BookingCargoModifier } from "../modules/bookings/entities/booking-cargo-modifier.entity";
import { CargoType } from "../modules/rule-engine/entities/cargo-type.entity";
import { ContainerType } from "../modules/rule-engine/entities/container-type.entity";
import { PriorityRule } from "../modules/rule-engine/entities/priority-rule.entity";
import { Rate } from "../modules/rule-engine/entities/rate.entity";
import { ServiceType } from "../modules/rule-engine/entities/service-type.entity";
import { ShippingLine } from "../modules/rule-engine/entities/shipping-line.entity";
import { SurchargeType } from "../modules/rule-engine/entities/surcharge-type.entity";
import { WeightLimitRule } from "../modules/rule-engine/entities/weight-limit-rule.entity";
import { Yard } from "../modules/rule-engine/entities/yard.entity";

const STAFF_USER_ID = "00000000-0000-0000-0000-000000000001";
const CEO_USER_ID = "00000000-0000-0000-0000-000000000002";

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

  constructor(private readonly dataSource: DataSource) { }

  async run(): Promise<void> {
    await this.dataSource.transaction(async (manager) => {
      const ctRepo = manager.getRepository(ContainerType);
      const stRepo = manager.getRepository(ServiceType);
      const yRepo = manager.getRepository(Yard);
      const slRepo = manager.getRepository(ShippingLine);
      const wlRepo = manager.getRepository(WeightLimitRule);
      const prRepo = manager.getRepository(PriorityRule);
      const rRepo = manager.getRepository(Rate);

      await this.upsertReferenceData(manager, ctRepo, stRepo, yRepo, slRepo);
      await this.seedWeightLimits(wlRepo, ctRepo);
      await this.seedPriorityRules(prRepo);
      const containerTypes = await ctRepo.find();
      const ctByCode = new Map(containerTypes.map((ct) => [ct.code, ct]));

      const rates = await this.seedRates(rRepo, ctByCode);
      const ratesByType = new Map<string, Rate[]>();
      for (const r of rates) {
        const key = `${r.rateType}|${r.currency}|${r.containerTypeId ?? ""}`;
        if (!ratesByType.has(key)) ratesByType.set(key, []);
        ratesByType.get(key)!.push(r);
      }

      await this.seedSurchargeTypes(manager, ratesByType);

      const yards = await yRepo.find();
      const yardByCode = new Map(yards.map((y) => [y.code, y]));
      const serviceTypes = await stRepo.find();
      const stByCode = new Map(serviceTypes.map((st) => [st.code, st]));
      const shippingLines = await slRepo.find();
      const slByCode = new Map(shippingLines.map((sl) => [sl.code, sl]));
      const cargoTypes = await manager.getRepository(CargoType).find();
      const cargoByCode = new Map(cargoTypes.map((c) => [c.code, c]));

      await this.seedDraftBookings(
        ctByCode,
        yardByCode,
        stByCode,
        slByCode,
        cargoByCode,
      );
    });

    this.logger.log("Seeded pricing data");
  }

  private async upsertReferenceData(
    manager: any,
    ctRepo: any,
    stRepo: any,
    yRepo: any,
    slRepo: any,
  ): Promise<void> {
    await yRepo.upsert(
      [
        {
          code: "DJIBOUTI",
          label: "Djibouti",
          country: "Djibouti",
          displayOrder: 1,
          isActive: true,
        },
        {
          code: "ADDIS_ABABA",
          label: "Addis Ababa",
          country: "Ethiopia",
          displayOrder: 2,
          isActive: true,
        },
        {
          code: "DIRE_DAWA",
          label: "Dire Dawa",
          country: "Ethiopia",
          displayOrder: 3,
          isActive: true,
        },
        {
          code: "MODJO",
          label: "Modjo",
          country: "Ethiopia",
          displayOrder: 4,
          isActive: true,
        },
      ],
      { conflictPaths: { code: true } },
    );

    await ctRepo.upsert(
      [
        {
          code: "20FT",
          label: "20FT Standard",
          sizeFt: 20,
          wagonsPerUnit: 1,
          isReefer: false,
          isOpenTop: false,
          isActive: true,
          displayOrder: 1,
        },
        {
          code: "40FT",
          label: "40FT Standard",
          sizeFt: 40,
          wagonsPerUnit: 1,
          isReefer: false,
          isOpenTop: false,
          isActive: true,
          displayOrder: 2,
        },
        {
          code: "20FT_REEFER",
          label: "20FT Reefer",
          sizeFt: 20,
          wagonsPerUnit: 1,
          isReefer: true,
          isOpenTop: false,
          isActive: true,
          displayOrder: 3,
        },
        {
          code: "40FT_REEFER",
          label: "40FT Reefer",
          sizeFt: 40,
          wagonsPerUnit: 1,
          isReefer: true,
          isOpenTop: false,
          isActive: true,
          displayOrder: 4,
        },
      ],
      { conflictPaths: { code: true } },
    );

    await stRepo.upsert(
      [
        {
          code: "RAIL_CONTAINER",
          serviceName: "Rail Container Service",
          description: "Standard rail container transport",
          canBeBookedAlone: true,
          includesFirstMile: false,
          includesLastMile: false,
          includesCustoms: false,
          priorityBonusPoints: 0,
          isActive: true,
          displayOrder: 1,
        },
        {
          code: "RAIL_FORWARDING",
          serviceName: "Rail Forwarding Service",
          description: "Rail transport with first/last mile and customs",
          canBeBookedAlone: true,
          includesFirstMile: true,
          includesLastMile: true,
          includesCustoms: true,
          priorityBonusPoints: 100,
          isActive: true,
          displayOrder: 2,
        },
        {
          code: "RAIL_BULK",
          serviceName: "Rail Bulk Transport",
          description: "Bulk commodity rail transport",
          canBeBookedAlone: true,
          includesFirstMile: false,
          includesLastMile: false,
          includesCustoms: false,
          priorityBonusPoints: 50,
          isActive: true,
          displayOrder: 3,
        },
      ],
      { conflictPaths: { code: true } },
    );

    await slRepo.upsert(
      [
        {
          code: "MAERSK",
          label: "Maersk Line",
          mappedToCode: "MAERSK",
          showExtraFeeNotice: true,
          isActive: true,
        },
        {
          code: "MSC",
          label: "MSC",
          mappedToCode: "MSC",
          showExtraFeeNotice: true,
          isActive: true,
        },
        {
          code: "CMA_CGM",
          label: "CMA CGM",
          mappedToCode: "CMA_CGM",
          showExtraFeeNotice: true,
          isActive: true,
        },
        {
          code: "COSCO",
          label: "COSCO Shipping",
          mappedToCode: "COSCO",
          showExtraFeeNotice: true,
          isActive: true,
        },
        {
          code: "OTHER",
          label: "Other Line",
          mappedToCode: null,
          showExtraFeeNotice: false,
          isActive: true,
        },
      ],
      { conflictPaths: { code: true } },
    );

    await manager.getRepository(CargoType).upsert(
      [
        {
          code: "GRAIN",
          cargoTypeName: "Grain / Cereals",
          requiresDirectorApproval: false,
          isActive: true,
          displayOrder: 1,
        },
        {
          code: "FERTILIZER",
          cargoTypeName: "Fertilizer",
          requiresDirectorApproval: false,
          isActive: true,
          displayOrder: 2,
        },
        {
          code: "CEMENT",
          cargoTypeName: "Cement / Clinker",
          requiresDirectorApproval: false,
          isActive: true,
          displayOrder: 3,
        },
        {
          code: "STEEL",
          cargoTypeName: "Steel / Rebar",
          requiresDirectorApproval: true,
          isActive: true,
          displayOrder: 4,
        },
        {
          code: "MACHINERY",
          cargoTypeName: "Heavy Machinery",
          requiresDirectorApproval: true,
          isActive: true,
          displayOrder: 5,
        },
        {
          code: "OTHER_BULK",
          cargoTypeName: "Other Bulk Cargo",
          requiresDirectorApproval: false,
          isActive: true,
          displayOrder: 6,
        },
      ],
      { conflictPaths: { code: true } },
    );
  }

  private async seedWeightLimits(wlRepo: any, ctRepo: any): Promise<void> {
    await wlRepo.createQueryBuilder().delete().execute();
    const twenty = await ctRepo.findOneByOrFail({ code: "20FT" });
    const forty = await ctRepo.findOneByOrFail({ code: "40FT" });
    const base = new Date("2026-01-01");
    await wlRepo.insert([
      {
        containerTypeId: twenty.id,
        tradeDirection: "IMPORT",
        maxVgmTons: 26,
        effectiveFrom: base,
      },
      {
        containerTypeId: twenty.id,
        tradeDirection: "EXPORT",
        maxVgmTons: 26,
        effectiveFrom: base,
      },
      {
        containerTypeId: forty.id,
        tradeDirection: "IMPORT",
        maxVgmTons: 28,
        effectiveFrom: base,
      },
      {
        containerTypeId: forty.id,
        tradeDirection: "EXPORT",
        maxVgmTons: 28,
        effectiveFrom: base,
      },
    ]);
    this.logger.log("Seeded weight limit rules");
  }

  private async seedPriorityRules(prRepo: any): Promise<void> {
    const existing = await prRepo.find({
      where: [{ code: "USD_PRIORITY" }, { code: "STANDARD_PRIORITY" }],
    });
    for (const r of existing) {
      await prRepo.remove(r);
    }
    await prRepo.save([
      prRepo.create({
        code: "USD_PRIORITY",
        label: "USD Payment Priority",
        score: 200,
        conditionCurrency: "USD",
        isActive: true,
      }),
      prRepo.create({
        code: "STANDARD_PRIORITY",
        label: "Standard Priority",
        score: 50,
        conditionCurrency: null,
        isActive: true,
      }),
    ]);
    this.logger.log("Seeded priority rules");
  }

  private async seedRates(
    rRepo: any,
    ctByCode: Map<string, any>,
  ): Promise<Rate[]> {
    const effectiveFrom = new Date("2026-01-01");
    const now = new Date();
        // await rRepo.createQueryBuilder().delete().execute();

    const rateData = [
      {
        rateType: "CONTAINER_IMPORT",
        containerTypeId: ctByCode.get("20FT")!.id,
        currency: "USD",
        rateValue: 800,
        rateUnit: "PER_CONTAINER",
      },
      {
        rateType: "CONTAINER_IMPORT",
        containerTypeId: ctByCode.get("40FT")!.id,
        currency: "USD",
        rateValue: 1200,
        rateUnit: "PER_CONTAINER",
      },
      {
        rateType: "CONTAINER_IMPORT",
        containerTypeId: ctByCode.get("20FT")!.id,
        currency: "ETB",
        rateValue: 45000,
        rateUnit: "PER_CONTAINER",
      },
      {
        rateType: "CONTAINER_IMPORT",
        containerTypeId: ctByCode.get("40FT")!.id,
        currency: "ETB",
        rateValue: 67000,
        rateUnit: "PER_CONTAINER",
      },
      {
        rateType: "CONTAINER_EXPORT",
        containerTypeId: ctByCode.get("20FT")!.id,
        currency: "USD",
        rateValue: 600,
        rateUnit: "PER_CONTAINER",
      },
      {
        rateType: "CONTAINER_EXPORT",
        containerTypeId: ctByCode.get("40FT")!.id,
        currency: "USD",
        rateValue: 900,
        rateUnit: "PER_CONTAINER",
      },
      {
        rateType: "CONTAINER_EXPORT",
        containerTypeId: ctByCode.get("20FT")!.id,
        currency: "ETB",
        rateValue: 34000,
        rateUnit: "PER_CONTAINER",
      },
      {
        rateType: "CONTAINER_EXPORT",
        containerTypeId: ctByCode.get("40FT")!.id,
        currency: "ETB",
        rateValue: 50000,
        rateUnit: "PER_CONTAINER",
      },
      {
        rateType: "INTERCITY_CONTAINER",
        containerTypeId: ctByCode.get("20FT")!.id,
        currency: "ETB",
        rateValue: 20000,
        rateUnit: "PER_CONTAINER",
      },
      {
        rateType: "INTERCITY_CONTAINER",
        containerTypeId: ctByCode.get("40FT")!.id,
        currency: "ETB",
        rateValue: 30000,
        rateUnit: "PER_CONTAINER",
      },
      {
        rateType: "CONTAINER_IMPORT",
        containerTypeId: null,
        currency: "USD",
        rateValue: 1000,
        rateUnit: "PER_CONTAINER",
      },
      {
        rateType: "CONTAINER_IMPORT",
        containerTypeId: null,
        currency: "ETB",
        rateValue: 56000,
        rateUnit: "PER_CONTAINER",
      },
      {
        rateType: "CONTAINER_EXPORT",
        containerTypeId: null,
        currency: "USD",
        rateValue: 750,
        rateUnit: "PER_CONTAINER",
      },
      {
        rateType: "CONTAINER_EXPORT",
        containerTypeId: null,
        currency: "ETB",
        rateValue: 42000,
        rateUnit: "PER_CONTAINER",
      },
      {
        rateType: "INTERCITY_CONTAINER",
        containerTypeId: null,
        currency: "ETB",
        rateValue: 25000,
        rateUnit: "PER_CONTAINER",
      },
      {
        rateType: "BULK_IMPORT",
        containerTypeId: null,
        currency: "USD",
        rateValue: 50,
        rateUnit: "PER_TON",
      },
      {
        rateType: "BULK_IMPORT",
        containerTypeId: null,
        currency: "ETB",
        rateValue: 2800,
        rateUnit: "PER_TON",
      },
      {
        rateType: "BULK_EXPORT",
        containerTypeId: null,
        currency: "USD",
        rateValue: 40,
        rateUnit: "PER_TON",
      },
      {
        rateType: "BULK_EXPORT",
        containerTypeId: null,
        currency: "ETB",
        rateValue: 2200,
        rateUnit: "PER_TON",
      },
      {
        rateType: "OVERWEIGHT_PER_TON",
        containerTypeId: null,
        currency: "USD",
        rateValue: 25,
        rateUnit: "PER_TON",
      },
      {
        rateType: "OVERWEIGHT_PER_TON",
        containerTypeId: null,
        currency: "ETB",
        rateValue: 1400,
        rateUnit: "PER_TON",
      },
      {
        rateType: "HAZARD_SURCHARGE",
        containerTypeId: null,
        currency: "USD",
        rateValue: 150,
        rateUnit: "FLAT",
      },
      {
        rateType: "HAZARD_SURCHARGE",
        containerTypeId: null,
        currency: "ETB",
        rateValue: 8500,
        rateUnit: "FLAT",
      },
      {
        rateType: "REEFER_SURCHARGE",
        containerTypeId: null,
        currency: "USD",
        rateValue: 200,
        rateUnit: "FLAT",
      },
      {
        rateType: "REEFER_SURCHARGE",
        containerTypeId: null,
        currency: "ETB",
        rateValue: 11000,
        rateUnit: "FLAT",
      },
      {
        rateType: "DOUBLE_HANDLING",
        containerTypeId: null,
        currency: "USD",
        rateValue: 100,
        rateUnit: "PER_CONTAINER",
      },
      {
        rateType: "DOUBLE_HANDLING",
        containerTypeId: null,
        currency: "ETB",
        rateValue: 5500,
        rateUnit: "PER_CONTAINER",
      },
      {
        rateType: "LASHING",
        containerTypeId: null,
        currency: "USD",
        rateValue: 50,
        rateUnit: "PER_CONTAINER",
      },
      {
        rateType: "LASHING",
        containerTypeId: null,
        currency: "ETB",
        rateValue: 2800,
        rateUnit: "PER_CONTAINER",
      },
    ];

    const entities = rateData.map((d) =>
      rRepo.create({
        ...d,
        status: "LIVE",
        proposedByStaffId: STAFF_USER_ID,
        approvedByCeoId: CEO_USER_ID,
        approvedAt: now,
        effectiveFrom,
      }),
    );
    return rRepo.save(entities);
  }

  private async seedSurchargeTypes(
    manager: any,
    ratesByType: Map<string, Rate[]>,
  ): Promise<void> {
    const surRepo = manager.getRepository(SurchargeType);
    const bcmRepo = manager.getRepository(BookingCargoModifier);
    await bcmRepo.createQueryBuilder().delete().execute();
    const findRate = (rateType: string, currency: string) => {
      const key = `${rateType}|${currency}|`;
      const rates = ratesByType.get(key);
      return rates?.[0];
    };

    const hazardRateUsd = findRate("HAZARD_SURCHARGE", "USD");
    const hazardRateEtb = findRate("HAZARD_SURCHARGE", "ETB");
    const reeferRateUsd = findRate("REEFER_SURCHARGE", "USD");
    const reeferRateEtb = findRate("REEFER_SURCHARGE", "ETB");
    const overweightRateUsd = findRate("OVERWEIGHT_PER_TON", "USD");
    const overweightRateEtb = findRate("OVERWEIGHT_PER_TON", "ETB");
    const shipLineRateUsd = findRate("DOUBLE_HANDLING", "USD");
    const shipLineRateEtb = findRate("DOUBLE_HANDLING", "ETB");
    const consolidRateUsd = findRate("LASHING", "USD");
    const consolidRateEtb = findRate("LASHING", "ETB");

    await surRepo.createQueryBuilder().delete().execute();
    await surRepo.save([
      surRepo.create({
        code: "HAZARDOUS_CARGO",
        label: "Hazardous Cargo",
        triggerCondition: "CARGO_FLAG_HAZARDOUS",
        rateId: hazardRateUsd?.id ?? hazardRateEtb?.id,
        isActive: true,
      }),
      surRepo.create({
        code: "REEFER_CARGO",
        label: "Reefer Cargo",
        triggerCondition: "CARGO_FLAG_REEFER",
        rateId: reeferRateUsd?.id ?? reeferRateEtb?.id,
        isActive: true,
      }),
      surRepo.create({
        code: "OVERWEIGHT_CARGO",
        label: "Overweight Cargo",
        triggerCondition: "VGM_EXCEEDS_LIMIT",
        rateId: overweightRateUsd?.id ?? overweightRateEtb?.id,
        isActive: true,
      }),
      surRepo.create({
        code: "SHIPPING_LINE_FEE",
        label: "Shipping Line Fee",
        triggerCondition: "SHIPPING_LINE_MAPPED",
        rateId: shipLineRateUsd?.id ?? shipLineRateEtb?.id,
        isActive: true,
      }),
      surRepo.create({
        code: "CONSOLIDATION_FEE",
        label: "Consolidation Fee",
        triggerCondition: "CONSOLIDATION_ENABLED",
        rateId: consolidRateUsd?.id ?? consolidRateEtb?.id,
        isActive: true,
      }),
    ]);
    this.logger.log("Seeded surcharge types");
  }

  private async seedDraftBookings(
    ctByCode: Map<string, any>,
    yardByCode: Map<string, any>,
    stByCode: Map<string, any>,
    slByCode: Map<string, any>,
    cargoByCode: Map<string, any>,
  ): Promise<void> {
    const djibouti = yardByCode.get("DJIBOUTI")!;
    const addis = yardByCode.get("ADDIS_ABABA")!;
    const railContainer = stByCode.get("RAIL_CONTAINER")!;
    const railBulk = stByCode.get("RAIL_BULK")!;
    const maersk = slByCode.get("MAERSK")!;
    const grain = cargoByCode.get("GRAIN")!;
    const twenty = ctByCode.get("20FT")!;
    const forty = ctByCode.get("40FT")!;
    const twentyReefer = ctByCode.get("20FT_REEFER")!;

    const drafts = [
      {
        reference: "BKG-PRICE-001",
        description: "Standard 20FT container import — base rail only",
        freightType: "CONTAINER" as const,
        tradeDirection: "IMPORT",
        paymentCurrency: "USD",
        serviceTypeId: railContainer.id,
        originYardId: djibouti.id,
        destinationYardId: addis.id,
        isHazardous: false,
        allowConsolidation: false,
        shippingLineId: null,
        cargoTypeId: null,
        cargoTotalWeightVgm: 250,
        containers: [
          { containerTypeId: twenty.id, quantity: 10, vgmPerUnitTons: 25 },
        ],
        expectedBaseRate: 800,
        expectedSurcharges: [],
      },
      {
        reference: "BKG-PRICE-002",
        description: "40FT container import + hazardous surcharge",
        freightType: "CONTAINER" as const,
        tradeDirection: "IMPORT",
        paymentCurrency: "USD",
        serviceTypeId: railContainer.id,
        originYardId: djibouti.id,
        destinationYardId: addis.id,
        isHazardous: true,
        allowConsolidation: false,
        shippingLineId: null,
        cargoTypeId: null,
        cargoTotalWeightVgm: 135,
        containers: [
          { containerTypeId: forty.id, quantity: 5, vgmPerUnitTons: 27 },
        ],
        expectedBaseRate: 1200,
        expectedSurcharges: ["HAZARDOUS_CARGO"],
      },
      {
        reference: "BKG-PRICE-003",
        description: "20FT container import + shipping line (ETB)",
        freightType: "CONTAINER" as const,
        tradeDirection: "IMPORT",
        paymentCurrency: "ETB",
        serviceTypeId: railContainer.id,
        originYardId: djibouti.id,
        destinationYardId: addis.id,
        isHazardous: false,
        allowConsolidation: false,
        shippingLineId: maersk.id,
        cargoTypeId: null,
        cargoTotalWeightVgm: 480,
        containers: [
          { containerTypeId: twenty.id, quantity: 20, vgmPerUnitTons: 24 },
        ],
        expectedBaseRate: 45000,
        expectedSurcharges: ["SHIPPING_LINE_FEE"],
      },
      {
        reference: "BKG-PRICE-004",
        description: "40FT container import + consolidation (USD)",
        freightType: "CONTAINER" as const,
        tradeDirection: "IMPORT",
        paymentCurrency: "USD",
        serviceTypeId: railContainer.id,
        originYardId: djibouti.id,
        destinationYardId: addis.id,
        isHazardous: false,
        allowConsolidation: true,
        shippingLineId: null,
        cargoTypeId: null,
        cargoTotalWeightVgm: 224,
        containers: [
          { containerTypeId: forty.id, quantity: 8, vgmPerUnitTons: 28 },
        ],
        expectedBaseRate: 1200,
        expectedSurcharges: ["CONSOLIDATION_FEE"],
      },
      {
        reference: "BKG-PRICE-005",
        description: "Bulk import — grain",
        freightType: "BULK" as const,
        tradeDirection: "IMPORT",
        paymentCurrency: "USD",
        serviceTypeId: railBulk.id,
        originYardId: djibouti.id,
        destinationYardId: addis.id,
        isHazardous: false,
        allowConsolidation: false,
        shippingLineId: null,
        cargoTypeId: grain.id,
        cargoTotalWeightVgm: 500,
        containers: [],
        expectedBaseRate: 50,
        expectedSurcharges: [],
      },
      {
        reference: "BKG-PRICE-006",
        description: "20FT reefer container import + reefer surcharge",
        freightType: "CONTAINER" as const,
        tradeDirection: "IMPORT",
        paymentCurrency: "USD",
        serviceTypeId: railContainer.id,
        originYardId: djibouti.id,
        destinationYardId: addis.id,
        isHazardous: false,
        allowConsolidation: false,
        shippingLineId: null,
        cargoTypeId: null,
        cargoTotalWeightVgm: 75,
        containers: [
          { containerTypeId: twentyReefer.id, quantity: 3, vgmPerUnitTons: 25 },
        ],
        expectedBaseRate: 800,
        expectedSurcharges: ["REEFER_CARGO"],
      },
      {
        reference: "BKG-PRICE-007",
        description: "20FT container import + overweight (30t > 26t limit)",
        freightType: "CONTAINER" as const,
        tradeDirection: "IMPORT",
        paymentCurrency: "USD",
        serviceTypeId: railContainer.id,
        originYardId: djibouti.id,
        destinationYardId: addis.id,
        isHazardous: false,
        allowConsolidation: false,
        shippingLineId: null,
        cargoTypeId: null,
        cargoTotalWeightVgm: 300,
        containers: [
          { containerTypeId: twenty.id, quantity: 10, vgmPerUnitTons: 30 },
        ],
        expectedBaseRate: 800,
        expectedSurcharges: ["OVERWEIGHT_CARGO"],
      },
    ];

    this.logger.log(`Seeded ${drafts.length} DRAFT bookings for pricing`);
  }
}
