import { MigrationInterface, QueryRunner } from 'typeorm';

/**
 * Fold the `surcharge_types` table into self-describing rates.
 *
 * Previously a surcharge was a separate row {trigger_condition, rate_id}. Now
 * each rate carries its own `applies_to` (friendly category) and `trigger`
 * (ALWAYS = base freight, otherwise a surcharge condition), plus an optional
 * `cargo_type_id` for bulk leaf commodities. The rule engine reads triggers
 * directly off LIVE rates, so the join table is no longer needed.
 *
 * This migration:
 *   1. adds applies_to / trigger / cargo_type_id to rates and backfills them
 *      from the existing rate_type matrix,
 *   2. repoints booking_cargo_modifier from surcharge_type_id → rate_id
 *      (backfilled via surcharge_types.rate_id),
 *   3. drops surcharge_types and its FK.
 */
export class FoldSurchargeTypesIntoRates1820000000004 implements MigrationInterface {
  name = 'FoldSurchargeTypesIntoRates1820000000004';

  public async up(queryRunner: QueryRunner): Promise<void> {
    // ── 1. New rate columns ────────────────────────────────────────────────
    await queryRunner.query(`
      ALTER TABLE freight.rates
        ADD COLUMN IF NOT EXISTS applies_to varchar(20) NOT NULL DEFAULT 'OTHER',
        ADD COLUMN IF NOT EXISTS "trigger" varchar(20) NOT NULL DEFAULT 'ALWAYS',
        ADD COLUMN IF NOT EXISTS cargo_type_id uuid NULL;
    `);

    await queryRunner.query(`
      ALTER TABLE freight.rates
        ADD CONSTRAINT "FK_rates_cargo_type_id"
        FOREIGN KEY (cargo_type_id) REFERENCES freight.cargo_types(id)
        ON DELETE SET NULL;
    `);

    await queryRunner.query(
      `CREATE INDEX IF NOT EXISTS "IDX_rates_trigger" ON freight.rates ("trigger");`,
    );

    // ── 1a. Backfill applies_to from the legacy rate_type matrix ────────────
    await queryRunner.query(`
      UPDATE freight.rates SET applies_to = CASE
        WHEN rate_type IN ('CONTAINER_IMPORT','CONTAINER_EXPORT','CONTAINER_WITH_RETURN') THEN 'CONTAINER'
        WHEN rate_type IN ('BULK_IMPORT','BULK_EXPORT') THEN 'BULK'
        WHEN rate_type IN ('INTERCITY_CONTAINER','INTERCITY_BULK') THEN 'INTERCITY'
        WHEN rate_type = 'FIRST_MILE' THEN 'FIRST_MILE'
        WHEN rate_type = 'LAST_MILE' THEN 'LAST_MILE'
        ELSE 'OTHER'
      END;
    `);

    // ── 1b. Backfill trigger from the legacy rate_type matrix ───────────────
    await queryRunner.query(`
      UPDATE freight.rates SET "trigger" = CASE
        WHEN rate_type = 'HAZARD_SURCHARGE' THEN 'HAZARDOUS'
        WHEN rate_type = 'REEFER_SURCHARGE' THEN 'REEFER'
        WHEN rate_type = 'OVERWEIGHT_PER_TON' THEN 'OVERWEIGHT'
        WHEN rate_type = 'DOUBLE_HANDLING' THEN 'SHIPPING_LINE'
        WHEN rate_type = 'LASHING' THEN 'CONSOLIDATION'
        WHEN rate_type = 'CANCELLATION_FEE' THEN 'CANCELLATION'
        WHEN rate_type = 'DEMURRAGE' THEN 'DEMURRAGE'
        WHEN rate_type = 'PIL_EXTRA_FEE' THEN 'PIL_EXTRA_FEE'
        ELSE 'ALWAYS'
      END;
    `);

    // Align the trigger to the actual surcharge_types mapping where one exists
    // (covers any rate wired as a surcharge with a non-obvious rate_type).
    await queryRunner.query(`
      UPDATE freight.rates r SET "trigger" = m.trig
      FROM (
        SELECT st.rate_id, CASE st.trigger_condition
          WHEN 'CARGO_FLAG_HAZARDOUS' THEN 'HAZARDOUS'
          WHEN 'CARGO_FLAG_REEFER'    THEN 'REEFER'
          WHEN 'VGM_EXCEEDS_LIMIT'    THEN 'OVERWEIGHT'
          WHEN 'SHIPPING_LINE_MAPPED' THEN 'SHIPPING_LINE'
          WHEN 'CONSOLIDATION_ENABLED' THEN 'CONSOLIDATION'
          ELSE 'ALWAYS'
        END AS trig
        FROM freight.surcharge_types st
        WHERE st.rate_id IS NOT NULL AND st.deleted_at IS NULL
      ) m
      WHERE r.id = m.rate_id AND m.trig <> 'ALWAYS';
    `);

    // ── 2. Repoint booking_cargo_modifier to rate_id ────────────────────────
    await queryRunner.query(`
      ALTER TABLE freight.booking_cargo_modifier
        ADD COLUMN IF NOT EXISTS rate_id uuid NULL;
    `);

    await queryRunner.query(`
      UPDATE freight.booking_cargo_modifier bcm
      SET rate_id = st.rate_id
      FROM freight.surcharge_types st
      WHERE bcm.surcharge_type_id = st.id AND st.rate_id IS NOT NULL;
    `);

    // Rows whose surcharge lost its rate can't be repointed — they reference a
    // now-defunct surcharge. Remove them so the NOT NULL + FK can be enforced.
    await queryRunner.query(`
      DELETE FROM freight.booking_cargo_modifier WHERE rate_id IS NULL;
    `);

    await queryRunner.query(`
      ALTER TABLE freight.booking_cargo_modifier
        ALTER COLUMN rate_id SET NOT NULL;
    `);

    // Drop the old FK + column + index for surcharge_type_id.
    await queryRunner.query(`
      ALTER TABLE freight.booking_cargo_modifier
        DROP CONSTRAINT IF EXISTS "FK_booking_cargo_modifier_surcharge_type_id";
    `);
    await queryRunner.query(
      `DROP INDEX IF EXISTS freight."IDX_booking_cargo_modifier_surcharge_type_id";`,
    );
    await queryRunner.query(`
      ALTER TABLE freight.booking_cargo_modifier
        DROP COLUMN IF EXISTS surcharge_type_id;
    `);

    await queryRunner.query(`
      ALTER TABLE freight.booking_cargo_modifier
        ADD CONSTRAINT "FK_booking_cargo_modifier_rate_id"
        FOREIGN KEY (rate_id) REFERENCES freight.rates(id);
    `);
    await queryRunner.query(
      `CREATE INDEX IF NOT EXISTS "IDX_booking_cargo_modifier_rate_id" ON freight.booking_cargo_modifier (rate_id);`,
    );

    // ── 3. Drop the surcharge_types table ───────────────────────────────────
    await queryRunner.query(`DROP TABLE IF EXISTS freight.surcharge_types;`);
  }

  public async down(queryRunner: QueryRunner): Promise<void> {
    // Recreate surcharge_types (structure only — data is not restored).
    await queryRunner.query(`
      CREATE TABLE IF NOT EXISTS freight.surcharge_types (
        id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
        code varchar(40) NOT NULL,
        label varchar(100),
        trigger_condition varchar(50),
        rate_id uuid,
        is_active boolean NOT NULL DEFAULT true,
        created_at timestamptz NOT NULL DEFAULT now(),
        updated_at timestamptz NOT NULL DEFAULT now(),
        deleted_at timestamptz
      );
    `);
    await queryRunner.query(
      `CREATE UNIQUE INDEX IF NOT EXISTS "IDX_surcharge_types_code" ON freight.surcharge_types (code);`,
    );

    // Restore booking_cargo_modifier.surcharge_type_id (nullable; not backfilled).
    await queryRunner.query(`
      ALTER TABLE freight.booking_cargo_modifier
        DROP CONSTRAINT IF EXISTS "FK_booking_cargo_modifier_rate_id";
    `);
    await queryRunner.query(
      `DROP INDEX IF EXISTS freight."IDX_booking_cargo_modifier_rate_id";`,
    );
    await queryRunner.query(`
      ALTER TABLE freight.booking_cargo_modifier
        ADD COLUMN IF NOT EXISTS surcharge_type_id uuid NULL;
    `);
    await queryRunner.query(`
      ALTER TABLE freight.booking_cargo_modifier DROP COLUMN IF EXISTS rate_id;
    `);

    // Drop the new rate columns.
    await queryRunner.query(
      `DROP INDEX IF EXISTS freight."IDX_rates_trigger";`,
    );
    await queryRunner.query(`
      ALTER TABLE freight.rates DROP CONSTRAINT IF EXISTS "FK_rates_cargo_type_id";
    `);
    await queryRunner.query(`
      ALTER TABLE freight.rates
        DROP COLUMN IF EXISTS cargo_type_id,
        DROP COLUMN IF EXISTS "trigger",
        DROP COLUMN IF EXISTS applies_to;
    `);
  }
}
