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

import { Facility } from '../modules/facilities/entities/facility.entity';
import { Warehouse } from '../modules/warehouses/entities/warehouse.entity';
import { WarehouseYard, type WarehouseYardType } from '../modules/warehouses/entities/warehouse-yard.entity';
import { WarehouseZone, type WarehouseZoneType } from '../modules/warehouses/entities/warehouse-zone.entity';

const INDODE_FACILITY = {
  code: 'INDODE_DRY_PORT',
  name: 'Indode Multipurpose Dry Port',
  description: 'Main facility for container and cargo handling',
  facilityType: 'DRY_PORT' as const,
  facilityStatus: 'ACTIVE' as const,
  locationName: 'Indode',
  country: 'Djibouti',
  city: 'Djibouti',
  address: 'Indode, Djibouti',
  latitude: 11.5447,
  longitude: 43.145,
  capacity: 50000,
  isActive: true,
  notes: 'Primary dry port for container consolidation and distribution',
};

const WAREHOUSES = [
  {
    name: 'Open Warehouse - Indode',
    code: 'INDODE_OPEN',
    type: 'OPEN_WAREHOUSE' as const,
    locationName: 'Indode Open',
    capacityWeight: 25000,
    capacityContainers: 500,
    maxWeight: 25000,
    maxVolume: 5000,
    status: 'ACTIVE' as const,
    isActive: true,
  },
  {
    name: 'Closed Warehouse - Indode',
    code: 'INDODE_CLOSED',
    type: 'CLOSED_WAREHOUSE' as const,
    locationName: 'Indode Closed',
    capacityWeight: 20000,
    capacityContainers: 400,
    maxWeight: 20000,
    maxVolume: 4000,
    status: 'ACTIVE' as const,
    isActive: true,
  },
];

const YARD_TYPES = [
  'CONTAINER_YARD',
  'BULK_YARD',
  'GENERAL_CARGO_YARD',
  'HAZARDOUS_YARD',
  'COLD_STORAGE_YARD',
] as const;

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

  constructor(private readonly dataSource: DataSource) {}

  async run(): Promise<void> {
    try {
      await this.dataSource.transaction(async (manager) => {
        const facilityRepo = manager.getRepository(Facility);
        const warehouseRepo = manager.getRepository(Warehouse);
        const yardRepo = manager.getRepository(WarehouseYard);
        const zoneRepo = manager.getRepository(WarehouseZone);

        // Ensure facility exists
        const facility = await facilityRepo.findOne({
          where: { code: INDODE_FACILITY.code },
        });

        if (facility) {
          this.logger.log('Indode facility already exists, skipping seed');
          return;
        }

        const newFacility = facilityRepo.create(INDODE_FACILITY);
        const savedFacility = await facilityRepo.save(newFacility);
        this.logger.log(`Created facility: ${savedFacility.code}`);

        // Create warehouses for the facility
        for (const warehouseData of WAREHOUSES) {
          try {
            const warehouse = await warehouseRepo.findOne({
              where: { code: warehouseData.code },
            });

            if (warehouse) {
              this.logger.log(`Warehouse ${warehouseData.code} already exists, skipping`);
              continue;
            }

            const newWarehouse = warehouseRepo.create({
              ...warehouseData,
              facilityId: savedFacility.id,
            });
            const savedWarehouse = await warehouseRepo.save(newWarehouse);
            this.logger.log(`Created warehouse: ${savedWarehouse.code} under facility ${savedFacility.code}`);

            // Create 11 yards per warehouse
            await this.createYardsForWarehouse(yardRepo, zoneRepo, savedWarehouse);
          } catch (warehouseError) {
            this.logger.warn(
              `Failed to create warehouse ${warehouseData.code}: ${warehouseError instanceof Error ? warehouseError.message : String(warehouseError)}`,
            );
          }
        }

        this.logger.log(
          'Indode Multipurpose Dry Port facility seeded successfully with 2 warehouses and 11 yards each',
        );
      });
    } catch (error) {
      this.logger.error(
        `IndodeFacilitySeeder error: ${error instanceof Error ? error.message : String(error)}`,
      );
    }
  }

  private async createYardsForWarehouse(
    yardRepo: any,
    zoneRepo: any,
    warehouse: Warehouse,
  ): Promise<void> {
    const baseCapacityWeight = 5000;
    const baseCapacityContainers = 100;
    const yardCount = 11;

    for (let i = 0; i < yardCount; i++) {
      const yardType: WarehouseYardType = i < YARD_TYPES.length ? YARD_TYPES[i] : 'GENERAL_CARGO_YARD';
      const yardCode = `${warehouse.code}_YARD_${String(i + 1).padStart(2, '0')}`;

      const existingYard = await yardRepo.findOne({ where: { code: yardCode } });
      if (existingYard) {
        this.logger.log(`Yard ${yardCode} already exists, skipping`);
        continue;
      }

      const yardData = {
        warehouseId: warehouse.id,
        name: `${warehouse.code} ${yardType.replace(/_/g, ' ')} ${String(i + 1).padStart(2, '0')}`,
        code: yardCode,
        type: yardType,
        capacityWeight: baseCapacityWeight,
        capacityContainers: baseCapacityContainers,
        maxWeight: baseCapacityWeight,
        maxVolume: baseCapacityWeight / 2,
        status: 'ACTIVE' as const,
        isActive: true,
      };

      const newYard = yardRepo.create(yardData);
      const savedYard = (await yardRepo.save(newYard)) as WarehouseYard;
      this.logger.log(`Created yard: ${savedYard.code}`);

      // Create default zone for the yard
      const zoneType: WarehouseZoneType = yardType.replace('_YARD', '_ZONE') as WarehouseZoneType;
      const zoneCode = `${savedYard.code}_ZONE_A`;

      const zoneData = {
        yardId: savedYard.id,
        name: `${savedYard.name} Zone A`,
        code: zoneCode,
        type: zoneType,
        capacityWeight: (baseCapacityWeight ?? 1000) / 2,
        capacityContainers: (baseCapacityContainers ?? 100) / 2,
        maxWeight: (baseCapacityWeight ?? 1000) / 2,
        maxVolume: (baseCapacityWeight ?? 500) / 2,
        status: 'ACTIVE' as const,
        isActive: true,
      };

      const newZone = zoneRepo.create(zoneData);
      await zoneRepo.save(newZone);
      this.logger.log(`Created zone: ${zoneCode}`);
    }
  }
}
