import { MigrationInterface, QueryRunner } from 'typeorm';

export class AddSchedulingAllocationEnhancements1750400000000
  implements MigrationInterface
{
  name = 'AddSchedulingAllocationEnhancements1750400000000';

  public async up(queryRunner: QueryRunner): Promise<void> {
    await queryRunner.query(`
      ALTER TABLE freight.bookings
      ADD COLUMN IF NOT EXISTS wagons_required NUMERIC(6,2) NULL,
      ADD COLUMN IF NOT EXISTS scheduling_status VARCHAR(30) NOT NULL DEFAULT 'NOT_SCHEDULED',
      ADD COLUMN IF NOT EXISTS hold_started_at TIMESTAMPTZ NULL,
      ADD COLUMN IF NOT EXISTS hold_expires_at TIMESTAMPTZ NULL,
      ADD COLUMN IF NOT EXISTS scheduled_at TIMESTAMPTZ NULL;
    `);

    await queryRunner.query(`
      ALTER TABLE freight.train_schedules
      ADD COLUMN IF NOT EXISTS train_number VARCHAR(20) NULL,
      ADD COLUMN IF NOT EXISTS direction VARCHAR(10) NULL,
      ADD COLUMN IF NOT EXISTS actual_departure_at TIMESTAMPTZ NULL,
      ADD COLUMN IF NOT EXISTS actual_arrival_at TIMESTAMPTZ NULL,
      ADD COLUMN IF NOT EXISTS prepared_by_user_id UUID NULL,
      ADD COLUMN IF NOT EXISTS checked_by_user_id UUID NULL,
      ADD COLUMN IF NOT EXISTS max_wagons INT NOT NULL DEFAULT 53;
    `);

    await queryRunner.query(`
      ALTER TABLE freight.train_set_wagons
      ADD COLUMN IF NOT EXISTS physical_wagon_id UUID NULL,
      ADD COLUMN IF NOT EXISTS status VARCHAR(20) NOT NULL DEFAULT 'PLANNED';
    `);

    await queryRunner.query(`
      ALTER TABLE freight.wagon_booking_allocations
      ADD COLUMN IF NOT EXISTS load_type VARCHAR(20) NULL,
      ADD COLUMN IF NOT EXISTS status VARCHAR(20) NOT NULL DEFAULT 'PLANNED',
      ADD COLUMN IF NOT EXISTS confirmed_at TIMESTAMPTZ NULL,
      ADD COLUMN IF NOT EXISTS confirmed_by_user_id UUID NULL;
    `);

    await queryRunner.query(`
      ALTER TABLE freight.wagon_types
      ADD COLUMN IF NOT EXISTS equated_length_m NUMERIC(10,3) NULL,
      ADD COLUMN IF NOT EXISTS tare_weight_tons NUMERIC(10,3) NULL,
      ADD COLUMN IF NOT EXISTS supports_container BOOLEAN NOT NULL DEFAULT false,
      ADD COLUMN IF NOT EXISTS max_container_gross_t NUMERIC(10,3) NULL;
    `);

    await queryRunner.query(`
      ALTER TABLE freight.wagons
      ADD COLUMN IF NOT EXISTS train_set_wagon_id UUID NULL,
      ADD COLUMN IF NOT EXISTS current_train_schedule_id UUID NULL;
    `);

    await queryRunner.query(`
      ALTER TABLE freight.containers
      ADD COLUMN IF NOT EXISTS booking_id UUID NULL,
      ADD COLUMN IF NOT EXISTS wagon_booking_allocation_id UUID NULL,
      ADD COLUMN IF NOT EXISTS booking_container_id UUID NULL;
    `);

    await queryRunner.query(`
      ALTER TABLE freight.cargoes
      ADD COLUMN IF NOT EXISTS wagon_booking_allocation_id UUID NULL,
      ADD COLUMN IF NOT EXISTS booking_id UUID NULL,
      ADD COLUMN IF NOT EXISTS load_type VARCHAR(20) NULL;
    `);

    await queryRunner.query(`
      ALTER TABLE freight.cargoes
      ALTER COLUMN container_id DROP NOT NULL;
    `);

    await queryRunner.query(`
      CREATE TABLE IF NOT EXISTS freight.wagon_allocation_container_items (
        id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
        wagon_booking_allocation_id UUID NOT NULL,
        booking_container_id UUID NULL,
        container_id UUID NULL,
        container_number VARCHAR(64) NULL,
        container_type_id UUID NOT NULL,
        position_on_wagon SMALLINT NULL,
        seal_number VARCHAR(64) NULL,
        chassis_number VARCHAR(64) NULL,
        gross_weight_tons NUMERIC(10,3) NULL,
        created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
        updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
        deleted_at TIMESTAMPTZ NULL,
        CONSTRAINT fk_waci_allocation FOREIGN KEY (wagon_booking_allocation_id)
          REFERENCES freight.wagon_booking_allocations(id) ON DELETE CASCADE,
        CONSTRAINT fk_waci_booking_container FOREIGN KEY (booking_container_id)
          REFERENCES freight.booking_container(id) ON DELETE SET NULL,
        CONSTRAINT fk_waci_container FOREIGN KEY (container_id)
          REFERENCES freight.containers(id) ON DELETE SET NULL,
        CONSTRAINT fk_waci_container_type FOREIGN KEY (container_type_id)
          REFERENCES freight.container_types(id)
      );
    `);

    await queryRunner.query(`
      CREATE TABLE IF NOT EXISTS freight.wagon_allocation_bulk_loads (
        id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
        wagon_booking_allocation_id UUID NOT NULL UNIQUE,
        booking_id UUID NOT NULL,
        cargo_type_id UUID NULL,
        cargo_description TEXT NULL,
        pricing_unit VARCHAR(20) NOT NULL DEFAULT 'PER_TON',
        quantity NUMERIC(12,3) NOT NULL DEFAULT 0,
        weight_tons NUMERIC(10,3) NOT NULL DEFAULT 0,
        truck_plate_number VARCHAR(32) NULL,
        created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
        updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
        deleted_at TIMESTAMPTZ NULL,
        CONSTRAINT fk_wabl_allocation FOREIGN KEY (wagon_booking_allocation_id)
          REFERENCES freight.wagon_booking_allocations(id) ON DELETE CASCADE,
        CONSTRAINT fk_wabl_booking FOREIGN KEY (booking_id)
          REFERENCES freight.bookings(id),
        CONSTRAINT fk_wabl_cargo_type FOREIGN KEY (cargo_type_id)
          REFERENCES freight.cargo_types(id) ON DELETE SET NULL
      );
    `);

    await queryRunner.query(`
      CREATE INDEX IF NOT EXISTS idx_bookings_scheduling_status
      ON freight.bookings(scheduling_status);
    `);
    await queryRunner.query(`
      CREATE INDEX IF NOT EXISTS idx_train_schedules_train_number
      ON freight.train_schedules(train_number);
    `);
    await queryRunner.query(`
      CREATE INDEX IF NOT EXISTS idx_train_set_wagons_physical_wagon
      ON freight.train_set_wagons(physical_wagon_id);
    `);
    await queryRunner.query(`
      CREATE INDEX IF NOT EXISTS idx_wagons_train_set_wagon_id
      ON freight.wagons(train_set_wagon_id);
    `);
    await queryRunner.query(`
      CREATE INDEX IF NOT EXISTS idx_wagons_current_train_schedule_id
      ON freight.wagons(current_train_schedule_id);
    `);
    await queryRunner.query(`
      CREATE INDEX IF NOT EXISTS idx_waci_allocation
      ON freight.wagon_allocation_container_items(wagon_booking_allocation_id);
    `);
    await queryRunner.query(`
      CREATE INDEX IF NOT EXISTS idx_wabl_booking
      ON freight.wagon_allocation_bulk_loads(booking_id);
    `);

    await queryRunner.query(`
      DO $$ BEGIN
        ALTER TABLE freight.train_set_wagons
          ADD CONSTRAINT fk_train_set_wagons_physical_wagon
          FOREIGN KEY (physical_wagon_id) REFERENCES freight.wagons(id) ON DELETE SET NULL;
      EXCEPTION WHEN duplicate_object THEN NULL;
      END $$;
    `);
    await queryRunner.query(`
      DO $$ BEGIN
        ALTER TABLE freight.wagons
          ADD CONSTRAINT fk_wagons_train_set_wagon
          FOREIGN KEY (train_set_wagon_id) REFERENCES freight.train_set_wagons(id) ON DELETE SET NULL;
      EXCEPTION WHEN duplicate_object THEN NULL;
      END $$;
    `);
    await queryRunner.query(`
      DO $$ BEGIN
        ALTER TABLE freight.wagons
          ADD CONSTRAINT fk_wagons_current_train_schedule
          FOREIGN KEY (current_train_schedule_id) REFERENCES freight.train_schedules(id) ON DELETE SET NULL;
      EXCEPTION WHEN duplicate_object THEN NULL;
      END $$;
    `);
    await queryRunner.query(`
      DO $$ BEGIN
        ALTER TABLE freight.containers
          ADD CONSTRAINT fk_containers_booking
          FOREIGN KEY (booking_id) REFERENCES freight.bookings(id) ON DELETE SET NULL;
      EXCEPTION WHEN duplicate_object THEN NULL;
      END $$;
    `);
    await queryRunner.query(`
      DO $$ BEGIN
        ALTER TABLE freight.containers
          ADD CONSTRAINT fk_containers_wagon_allocation
          FOREIGN KEY (wagon_booking_allocation_id) REFERENCES freight.wagon_booking_allocations(id) ON DELETE SET NULL;
      EXCEPTION WHEN duplicate_object THEN NULL;
      END $$;
    `);
    await queryRunner.query(`
      DO $$ BEGIN
        ALTER TABLE freight.containers
          ADD CONSTRAINT fk_containers_booking_container
          FOREIGN KEY (booking_container_id) REFERENCES freight.booking_container(id) ON DELETE SET NULL;
      EXCEPTION WHEN duplicate_object THEN NULL;
      END $$;
    `);
    await queryRunner.query(`
      DO $$ BEGIN
        ALTER TABLE freight.cargoes
          ADD CONSTRAINT fk_cargoes_wagon_allocation
          FOREIGN KEY (wagon_booking_allocation_id) REFERENCES freight.wagon_booking_allocations(id) ON DELETE SET NULL;
      EXCEPTION WHEN duplicate_object THEN NULL;
      END $$;
    `);
    await queryRunner.query(`
      DO $$ BEGIN
        ALTER TABLE freight.cargoes
          ADD CONSTRAINT fk_cargoes_booking
          FOREIGN KEY (booking_id) REFERENCES freight.bookings(id) ON DELETE SET NULL;
      EXCEPTION WHEN duplicate_object THEN NULL;
      END $$;
    `);

    await queryRunner.query(`
      UPDATE freight.wagon_types SET
        equated_length_m = 1.3,
        tare_weight_tons = 22.4,
        supports_container = true,
        max_container_gross_t = 30.48
      WHERE code = 'NW5';
    `);
    await queryRunner.query(`
      UPDATE freight.wagon_types SET
        equated_length_m = 1.6,
        tare_weight_tons = 25.2,
        supports_container = false
      WHERE code = 'PW2';
    `);
    await queryRunner.query(`
      UPDATE freight.wagon_types SET
        equated_length_m = 1.5,
        tare_weight_tons = 25.2,
        supports_container = false
      WHERE code = 'KW2';
    `);
    await queryRunner.query(`
      UPDATE freight.wagon_types SET
        equated_length_m = 1.3,
        tare_weight_tons = 23.4,
        supports_container = false
      WHERE code = 'CW3';
    `);
    await queryRunner.query(`
      UPDATE freight.wagon_types SET
        equated_length_m = 1.3,
        tare_weight_tons = 24.8,
        supports_container = false
      WHERE code = 'CW4';
    `);
  }

  public async down(queryRunner: QueryRunner): Promise<void> {
    await queryRunner.query(`DROP TABLE IF EXISTS freight.wagon_allocation_bulk_loads;`);
    await queryRunner.query(`DROP TABLE IF EXISTS freight.wagon_allocation_container_items;`);

    await queryRunner.query(`
      ALTER TABLE freight.cargoes
      ALTER COLUMN container_id SET NOT NULL;
    `);

    await queryRunner.query(`
      ALTER TABLE freight.bookings
      DROP COLUMN IF EXISTS wagons_required,
      DROP COLUMN IF EXISTS scheduling_status,
      DROP COLUMN IF EXISTS hold_started_at,
      DROP COLUMN IF EXISTS hold_expires_at,
      DROP COLUMN IF EXISTS scheduled_at;
    `);
    await queryRunner.query(`
      ALTER TABLE freight.train_schedules
      DROP COLUMN IF EXISTS train_number,
      DROP COLUMN IF EXISTS direction,
      DROP COLUMN IF EXISTS actual_departure_at,
      DROP COLUMN IF EXISTS actual_arrival_at,
      DROP COLUMN IF EXISTS prepared_by_user_id,
      DROP COLUMN IF EXISTS checked_by_user_id,
      DROP COLUMN IF EXISTS max_wagons;
    `);
    await queryRunner.query(`
      ALTER TABLE freight.train_set_wagons
      DROP COLUMN IF EXISTS physical_wagon_id,
      DROP COLUMN IF EXISTS status;
    `);
    await queryRunner.query(`
      ALTER TABLE freight.wagon_booking_allocations
      DROP COLUMN IF EXISTS load_type,
      DROP COLUMN IF EXISTS status,
      DROP COLUMN IF EXISTS confirmed_at,
      DROP COLUMN IF EXISTS confirmed_by_user_id;
    `);
    await queryRunner.query(`
      ALTER TABLE freight.wagon_types
      DROP COLUMN IF EXISTS equated_length_m,
      DROP COLUMN IF EXISTS tare_weight_tons,
      DROP COLUMN IF EXISTS supports_container,
      DROP COLUMN IF EXISTS max_container_gross_t;
    `);
    await queryRunner.query(`
      ALTER TABLE freight.wagons
      DROP COLUMN IF EXISTS train_set_wagon_id,
      DROP COLUMN IF EXISTS current_train_schedule_id;
    `);
    await queryRunner.query(`
      ALTER TABLE freight.containers
      DROP COLUMN IF EXISTS booking_id,
      DROP COLUMN IF EXISTS wagon_booking_allocation_id,
      DROP COLUMN IF EXISTS booking_container_id;
    `);
    await queryRunner.query(`
      ALTER TABLE freight.cargoes
      DROP COLUMN IF EXISTS wagon_booking_allocation_id,
      DROP COLUMN IF EXISTS booking_id,
      DROP COLUMN IF EXISTS load_type;
    `);
  }
}
