import { MigrationInterface, QueryRunner } from 'typeorm';

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

  public async up(queryRunner: QueryRunner): Promise<void> {
    const publicCustomersExists = await queryRunner.query(`
      SELECT EXISTS (
        SELECT 1 FROM information_schema.tables
        WHERE table_schema = 'public' AND table_name = 'customers'
      ) AS exists
    `);
    const hasPublicCustomers = Boolean(publicCustomersExists[0]?.exists);

    // ── freight.bookings: nullable FK cleanup ─────────────────────────────
    await queryRunner.query(`
      UPDATE freight.bookings b
      SET train_id = NULL
      WHERE b.train_id IS NOT NULL
        AND NOT EXISTS (SELECT 1 FROM freight.trains t WHERE t.id = b.train_id);
    `);
    await queryRunner.query(`
      UPDATE freight.bookings b
      SET previous_contract_id = NULL
      WHERE b.previous_contract_id IS NOT NULL
        AND NOT EXISTS (SELECT 1 FROM freight.bookings pb WHERE pb.id = b.previous_contract_id);
    `);
    await queryRunner.query(`
      UPDATE freight.bookings b
      SET consolidation_partner_id = NULL
      WHERE b.consolidation_partner_id IS NOT NULL
        AND NOT EXISTS (SELECT 1 FROM freight.bookings pb WHERE pb.id = b.consolidation_partner_id);
    `);

    // Legacy DBs only: public.customers is created/moved in MoveCustomersToFreightSchema (174890).
    if (hasPublicCustomers) {
      await queryRunner.query(`
        DELETE FROM freight.booking_cargo_modifier bcm
        USING freight.bookings b
        WHERE bcm.booking_id = b.id
          AND NOT EXISTS (SELECT 1 FROM public.customers c WHERE c.id = b.customer_id);
      `);
      await queryRunner.query(`
        DELETE FROM freight.booking_approval_step bas
        USING freight.bookings b
        WHERE bas.booking_id = b.id
          AND NOT EXISTS (SELECT 1 FROM public.customers c WHERE c.id = b.customer_id);
      `);
      await queryRunner.query(`
        DELETE FROM freight.booking_rate_snapshot brs
        USING freight.bookings b
        WHERE brs.booking_id = b.id
          AND NOT EXISTS (SELECT 1 FROM public.customers c WHERE c.id = b.customer_id);
      `);
      await queryRunner.query(`
        DELETE FROM freight.booking_container bc
        USING freight.bookings b
        WHERE bc.booking_id = b.id
          AND NOT EXISTS (SELECT 1 FROM public.customers c WHERE c.id = b.customer_id);
      `);
      await queryRunner.query(`
        DELETE FROM freight.bookings b
        WHERE NOT EXISTS (SELECT 1 FROM public.customers c WHERE c.id = b.customer_id);
      `);

      await queryRunner.query(`
        DO $$ BEGIN
          ALTER TABLE freight.bookings
            ADD CONSTRAINT "FK_bookings_customer_id"
            FOREIGN KEY (customer_id)
            REFERENCES public.customers(id)
            ON DELETE RESTRICT;
        EXCEPTION
          WHEN duplicate_object THEN NULL;
        END $$;
      `);
    }

    // ── freight.bookings FKs ────────────────────────────────────────────
    await queryRunner.query(`
      DO $$ BEGIN
        ALTER TABLE freight.bookings
          ADD CONSTRAINT "FK_bookings_train_id"
          FOREIGN KEY (train_id)
          REFERENCES freight.trains(id)
          ON DELETE SET NULL;
      EXCEPTION
        WHEN duplicate_object THEN NULL;
      END $$;
    `);

    await queryRunner.query(`
      DO $$ BEGIN
        ALTER TABLE freight.bookings
          ADD CONSTRAINT "FK_bookings_previous_contract_id"
          FOREIGN KEY (previous_contract_id)
          REFERENCES freight.bookings(id)
          ON DELETE SET NULL;
      EXCEPTION
        WHEN duplicate_object THEN NULL;
      END $$;
    `);

    await queryRunner.query(`
      DO $$ BEGIN
        ALTER TABLE freight.bookings
          ADD CONSTRAINT "FK_bookings_consolidation_partner_id"
          FOREIGN KEY (consolidation_partner_id)
          REFERENCES freight.bookings(id)
          ON DELETE SET NULL;
      EXCEPTION
        WHEN duplicate_object THEN NULL;
      END $$;
    `);

    // ── freight.booking_container ─────────────────────────────────────────
    await queryRunner.query(`
      UPDATE freight.booking_container bc
      SET weight_limit_rule_id = NULL
      WHERE bc.weight_limit_rule_id IS NOT NULL
        AND NOT EXISTS (
          SELECT 1 FROM freight.weight_limit_rules wlr WHERE wlr.id = bc.weight_limit_rule_id
        );
    `);

    await queryRunner.query(`
      DELETE FROM freight.booking_container bc
      WHERE NOT EXISTS (SELECT 1 FROM freight.container_types ct WHERE ct.id = bc.container_type_id);
    `);

    await queryRunner.query(`
      DO $$ BEGIN
        ALTER TABLE freight.booking_container
          ADD CONSTRAINT "FK_booking_container_container_type_id"
          FOREIGN KEY (container_type_id)
          REFERENCES freight.container_types(id)
          ON DELETE RESTRICT;
      EXCEPTION
        WHEN duplicate_object THEN NULL;
      END $$;
    `);

    await queryRunner.query(`
      DO $$ BEGIN
        ALTER TABLE freight.booking_container
          ADD CONSTRAINT "FK_booking_container_weight_limit_rule_id"
          FOREIGN KEY (weight_limit_rule_id)
          REFERENCES freight.weight_limit_rules(id)
          ON DELETE SET NULL;
      EXCEPTION
        WHEN duplicate_object THEN NULL;
      END $$;
    `);

    // ── freight.booking_rate_snapshot ─────────────────────────────────────
    await queryRunner.query(`
      DELETE FROM freight.booking_cargo_modifier bcm
      USING freight.booking_rate_snapshot brs
      WHERE bcm.rate_snapshot_id = brs.id
        AND (
          NOT EXISTS (SELECT 1 FROM freight.bookings b WHERE b.id = brs.booking_id)
          OR NOT EXISTS (SELECT 1 FROM freight.rates r WHERE r.id = brs.rate_id)
        );
    `);
    await queryRunner.query(`
      DELETE FROM freight.booking_rate_snapshot brs
      WHERE NOT EXISTS (SELECT 1 FROM freight.bookings b WHERE b.id = brs.booking_id)
         OR NOT EXISTS (SELECT 1 FROM freight.rates r WHERE r.id = brs.rate_id);
    `);

    await queryRunner.query(`
      DO $$ BEGIN
        ALTER TABLE freight.booking_rate_snapshot
          ADD CONSTRAINT "FK_booking_rate_snapshot_booking_id"
          FOREIGN KEY (booking_id)
          REFERENCES freight.bookings(id)
          ON DELETE CASCADE;
      EXCEPTION
        WHEN duplicate_object THEN NULL;
      END $$;
    `);

    await queryRunner.query(`
      DO $$ BEGIN
        ALTER TABLE freight.booking_rate_snapshot
          ADD CONSTRAINT "FK_booking_rate_snapshot_rate_id"
          FOREIGN KEY (rate_id)
          REFERENCES freight.rates(id)
          ON DELETE RESTRICT;
      EXCEPTION
        WHEN duplicate_object THEN NULL;
      END $$;
    `);

    // ── freight.booking_approval_step ─────────────────────────────────────
    await queryRunner.query(`
      DELETE FROM freight.booking_approval_step bas
      WHERE NOT EXISTS (SELECT 1 FROM freight.bookings b WHERE b.id = bas.booking_id)
         OR NOT EXISTS (SELECT 1 FROM freight.approval_rules ar WHERE ar.id = bas.approval_rule_id);
    `);

    await queryRunner.query(`
      DO $$ BEGIN
        ALTER TABLE freight.booking_approval_step
          ADD CONSTRAINT "FK_booking_approval_step_booking_id"
          FOREIGN KEY (booking_id)
          REFERENCES freight.bookings(id)
          ON DELETE CASCADE;
      EXCEPTION
        WHEN duplicate_object THEN NULL;
      END $$;
    `);

    await queryRunner.query(`
      DO $$ BEGIN
        ALTER TABLE freight.booking_approval_step
          ADD CONSTRAINT "FK_booking_approval_step_approval_rule_id"
          FOREIGN KEY (approval_rule_id)
          REFERENCES freight.approval_rules(id)
          ON DELETE RESTRICT;
      EXCEPTION
        WHEN duplicate_object THEN NULL;
      END $$;
    `);

    // ── freight.booking_cargo_modifier ────────────────────────────────────
    await queryRunner.query(`
      DELETE FROM freight.booking_cargo_modifier bcm
      WHERE NOT EXISTS (SELECT 1 FROM freight.bookings b WHERE b.id = bcm.booking_id)
         OR NOT EXISTS (SELECT 1 FROM freight.surcharge_types st WHERE st.id = bcm.surcharge_type_id)
         OR NOT EXISTS (
           SELECT 1 FROM freight.booking_rate_snapshot brs WHERE brs.id = bcm.rate_snapshot_id
         );
    `);

    await queryRunner.query(`
      DO $$ BEGIN
        ALTER TABLE freight.booking_cargo_modifier
          ADD CONSTRAINT "FK_booking_cargo_modifier_booking_id"
          FOREIGN KEY (booking_id)
          REFERENCES freight.bookings(id)
          ON DELETE CASCADE;
      EXCEPTION
        WHEN duplicate_object THEN NULL;
      END $$;
    `);

    await queryRunner.query(`
      DO $$ BEGIN
        ALTER TABLE freight.booking_cargo_modifier
          ADD CONSTRAINT "FK_booking_cargo_modifier_surcharge_type_id"
          FOREIGN KEY (surcharge_type_id)
          REFERENCES freight.surcharge_types(id)
          ON DELETE RESTRICT;
      EXCEPTION
        WHEN duplicate_object THEN NULL;
      END $$;
    `);

    await queryRunner.query(`
      DO $$ BEGIN
        ALTER TABLE freight.booking_cargo_modifier
          ADD CONSTRAINT "FK_booking_cargo_modifier_rate_snapshot_id"
          FOREIGN KEY (rate_snapshot_id)
          REFERENCES freight.booking_rate_snapshot(id)
          ON DELETE RESTRICT;
      EXCEPTION
        WHEN duplicate_object THEN NULL;
      END $$;
    `);
  }

  public async down(queryRunner: QueryRunner): Promise<void> {
    await queryRunner.query(`
      ALTER TABLE freight.booking_cargo_modifier
        DROP CONSTRAINT IF EXISTS "FK_booking_cargo_modifier_rate_snapshot_id";
    `);
    await queryRunner.query(`
      ALTER TABLE freight.booking_cargo_modifier
        DROP CONSTRAINT IF EXISTS "FK_booking_cargo_modifier_surcharge_type_id";
    `);
    await queryRunner.query(`
      ALTER TABLE freight.booking_cargo_modifier
        DROP CONSTRAINT IF EXISTS "FK_booking_cargo_modifier_booking_id";
    `);

    await queryRunner.query(`
      ALTER TABLE freight.booking_approval_step
        DROP CONSTRAINT IF EXISTS "FK_booking_approval_step_approval_rule_id";
    `);
    await queryRunner.query(`
      ALTER TABLE freight.booking_approval_step
        DROP CONSTRAINT IF EXISTS "FK_booking_approval_step_booking_id";
    `);

    await queryRunner.query(`
      ALTER TABLE freight.booking_rate_snapshot
        DROP CONSTRAINT IF EXISTS "FK_booking_rate_snapshot_rate_id";
    `);
    await queryRunner.query(`
      ALTER TABLE freight.booking_rate_snapshot
        DROP CONSTRAINT IF EXISTS "FK_booking_rate_snapshot_booking_id";
    `);

    await queryRunner.query(`
      ALTER TABLE freight.booking_container
        DROP CONSTRAINT IF EXISTS "FK_booking_container_weight_limit_rule_id";
    `);
    await queryRunner.query(`
      ALTER TABLE freight.booking_container
        DROP CONSTRAINT IF EXISTS "FK_booking_container_container_type_id";
    `);

    await queryRunner.query(`
      ALTER TABLE freight.bookings
        DROP CONSTRAINT IF EXISTS "FK_bookings_consolidation_partner_id";
    `);
    await queryRunner.query(`
      ALTER TABLE freight.bookings
        DROP CONSTRAINT IF EXISTS "FK_bookings_previous_contract_id";
    `);
    await queryRunner.query(`
      ALTER TABLE freight.bookings
        DROP CONSTRAINT IF EXISTS "FK_bookings_train_id";
    `);
    await queryRunner.query(`
      ALTER TABLE freight.bookings
        DROP CONSTRAINT IF EXISTS "FK_bookings_customer_id";
    `);
  }
}
