import { MigrationInterface, QueryRunner } from 'typeorm';

/**
 * Scope base rail freight to a route (origin yard → destination yard).
 *
 * Until now a base-freight rate was keyed by direction + container/bulk scope
 * only, so "container import" cost the same whether the box was railed to Dire
 * Dawa or to Mojo. Rates now carry the yard pair the price is quoted for, which
 * is what the business actually sells: `container import, Djibouti → Dire Dawa,
 * 500 USD`.
 *
 * Existing base-freight rates predate the yard pair and cannot be backfilled —
 * there is no way to know which route each was meant for. They are retired
 * (SUPERSEDED + soft-deleted) rather than deleted, because booking_rate_snapshot
 * and rate_change_requests hold FKs to them (RESTRICT) and those rows are price
 * history. Retiring drops them out of pricing and the admin UI just the same;
 * the yard-scoped replacements must be re-entered.
 *
 * Surcharges, first-mile and last-mile rates are untouched: they are not
 * route-scoped and keep NULL yards.
 */
export class AddRateYardScope2320000000000 implements MigrationInterface {
  name = 'AddRateYardScope2320000000000';

  public async up(queryRunner: QueryRunner): Promise<void> {
    // ── 1. Yard columns + FKs ──────────────────────────────────────────────
    await queryRunner.query(`
      ALTER TABLE freight.rates
        ADD COLUMN IF NOT EXISTS origin_yard_id uuid NULL,
        ADD COLUMN IF NOT EXISTS destination_yard_id uuid NULL;
    `);

    await queryRunner.query(`
      DO $$ BEGIN
        IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'FK_rates_origin_yard_id') THEN
          ALTER TABLE freight.rates
            ADD CONSTRAINT "FK_rates_origin_yard_id"
            FOREIGN KEY (origin_yard_id) REFERENCES freight.yards(id);
        END IF;
        IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'FK_rates_destination_yard_id') THEN
          ALTER TABLE freight.rates
            ADD CONSTRAINT "FK_rates_destination_yard_id"
            FOREIGN KEY (destination_yard_id) REFERENCES freight.yards(id);
        END IF;
      END $$;
    `);

    await queryRunner.query(
      `CREATE INDEX IF NOT EXISTS "IDX_rates_origin_yard_id" ON freight.rates (origin_yard_id);`,
    );
    await queryRunner.query(
      `CREATE INDEX IF NOT EXISTS "IDX_rates_destination_yard_id" ON freight.rates (destination_yard_id);`,
    );

    // ── 2. Retire route-less base freight ──────────────────────────────────
    // Soft-delete, not DELETE: booking_rate_snapshot.rate_id is ON DELETE
    // RESTRICT and those snapshots are what past bookings were charged.
    await queryRunner.query(`
      UPDATE freight.rates
      SET status = 'SUPERSEDED',
          deleted_at = now(),
          updated_at = now()
      WHERE deleted_at IS NULL
        AND "trigger" = 'ALWAYS'
        AND applies_to IN ('BULK', 'CONTAINER', 'INTERCITY');
    `);

    // ── 3. Route is part of a rate's identity ──────────────────────────────
    // Two rates may now share rateType + scope + unit as long as they price
    // different legs, so the yard pair joins the uniqueness tuple.
    await queryRunner.query(`DROP INDEX IF EXISTS freight."UQ_rates_pattern";`);
    await queryRunner.query(`
      CREATE UNIQUE INDEX IF NOT EXISTS "UQ_rates_pattern"
      ON freight.rates (
        rate_type,
        COALESCE(container_type_id, '00000000-0000-0000-0000-000000000000'::uuid),
        COALESCE(cargo_type_id, '00000000-0000-0000-0000-000000000000'::uuid),
        COALESCE(trade_direction, ''),
        COALESCE(origin_yard_id, '00000000-0000-0000-0000-000000000000'::uuid),
        COALESCE(destination_yard_id, '00000000-0000-0000-0000-000000000000'::uuid),
        rate_unit
      )
      WHERE deleted_at IS NULL AND status <> 'SUPERSEDED';
    `);

    // ── 4. Base freight must carry a route; nothing else may ───────────────
    // Retired rows are exempt — they are the route-less rates step 2 just
    // superseded, and they must stay readable for snapshot history.
    await queryRunner.query(`
      DO $$ BEGIN
        IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'CK_rates_yard_scope') THEN
          ALTER TABLE freight.rates
            ADD CONSTRAINT "CK_rates_yard_scope" CHECK (
              deleted_at IS NOT NULL
              OR status = 'SUPERSEDED'
              OR CASE
                   WHEN "trigger" = 'ALWAYS' AND applies_to IN ('BULK', 'CONTAINER', 'INTERCITY')
                     THEN origin_yard_id IS NOT NULL AND destination_yard_id IS NOT NULL
                   ELSE origin_yard_id IS NULL AND destination_yard_id IS NULL
                 END
            );
        END IF;
      END $$;
    `);
  }

  public async down(queryRunner: QueryRunner): Promise<void> {
    // The retired rates are not un-superseded: which route each belonged to was
    // never recorded, so reviving them would restore rates that price the wrong
    // legs. Down only reverses the schema.
    await queryRunner.query(
      `ALTER TABLE freight.rates DROP CONSTRAINT IF EXISTS "CK_rates_yard_scope";`,
    );
    await queryRunner.query(`DROP INDEX IF EXISTS freight."UQ_rates_pattern";`);
    await queryRunner.query(`
      CREATE UNIQUE INDEX IF NOT EXISTS "UQ_rates_pattern"
      ON freight.rates (
        rate_type,
        COALESCE(container_type_id, '00000000-0000-0000-0000-000000000000'::uuid),
        COALESCE(cargo_type_id, '00000000-0000-0000-0000-000000000000'::uuid),
        COALESCE(trade_direction, ''),
        rate_unit
      )
      WHERE deleted_at IS NULL AND status <> 'SUPERSEDED';
    `);
    await queryRunner.query(`DROP INDEX IF EXISTS freight."IDX_rates_destination_yard_id";`);
    await queryRunner.query(`DROP INDEX IF EXISTS freight."IDX_rates_origin_yard_id";`);
    await queryRunner.query(
      `ALTER TABLE freight.rates DROP CONSTRAINT IF EXISTS "FK_rates_destination_yard_id";`,
    );
    await queryRunner.query(
      `ALTER TABLE freight.rates DROP CONSTRAINT IF EXISTS "FK_rates_origin_yard_id";`,
    );
    await queryRunner.query(`
      ALTER TABLE freight.rates
        DROP COLUMN IF EXISTS destination_yard_id,
        DROP COLUMN IF EXISTS origin_yard_id;
    `);
  }
}
