import { type MigrationInterface, type QueryRunner } from 'typeorm';

/**
 * Schema drift catch-up — adds columns that were tacked on to existing
 * tables via `synchronize: true` in dev but never captured in a migration.
 * Sister migration to 1776000000000-CatchUpMissingTables (which adds
 * the entirely-missing tables); this one handles columns added to
 * tables that DO exist in InitialSchema.
 *
 * Idempotent: every statement uses `ADD COLUMN IF NOT EXISTS`, so on
 * any DB where these columns already exist it's a no-op.
 *
 * Constraint policy: columns are added without NOT NULL even when
 * locally they're NOT NULL. Reasoning: a fresh UAT install has empty
 * tables, so an ADD with NOT NULL + DEFAULT works. But if any row
 * already exists (partial-state recovery), NOT NULL would crash. The
 * entities enforce non-null at the application layer; the schema is
 * marginally looser to keep this migration safe to re-run anywhere.
 */
export class SchemaDriftCatchUp1776000000001 implements MigrationInterface {
  public async up(queryRunner: QueryRunner): Promise<void> {
    // ── users ──────────────────────────────────────────────────
    await queryRunner.query(
      `ALTER TABLE "users" ADD COLUMN IF NOT EXISTS "active_destination_id" uuid`,
    );

    // ── tenant_settings — already covered by Initial, no drift ─

    // ── destinations ───────────────────────────────────────────
    await queryRunner.query(
      `ALTER TABLE "destinations" ADD COLUMN IF NOT EXISTS "tax_types" jsonb`,
    );
    await queryRunner.query(
      `ALTER TABLE "destinations" ADD COLUMN IF NOT EXISTS "usd_to_base_rate" numeric(12,6)`,
    );
    await queryRunner.query(
      `ALTER TABLE "destinations" ADD COLUMN IF NOT EXISTS "rate_updated_at" timestamp without time zone`,
    );

    // ── service_types ──────────────────────────────────────────
    await queryRunner.query(
      `ALTER TABLE "service_types" ADD COLUMN IF NOT EXISTS "sort_order" integer NOT NULL DEFAULT 0`,
    );

    // ── trip_sources ───────────────────────────────────────────
    await queryRunner.query(
      `ALTER TABLE "trip_sources" ADD COLUMN IF NOT EXISTS "short_code" character varying(50)`,
    );

    // ── hotels ─────────────────────────────────────────────────
    await queryRunner.query(
      `ALTER TABLE "hotels" ADD COLUMN IF NOT EXISTS "short_code" character varying(50)`,
    );

    // ── transport_services — return-trip + airport flag ───────
    await queryRunner.query(
      `ALTER TABLE "transport_services" ADD COLUMN IF NOT EXISTS "return_from_city" character varying(100)`,
    );
    await queryRunner.query(
      `ALTER TABLE "transport_services" ADD COLUMN IF NOT EXISTS "return_from_address" character varying(500)`,
    );
    await queryRunner.query(
      `ALTER TABLE "transport_services" ADD COLUMN IF NOT EXISTS "return_to_city" character varying(100)`,
    );
    await queryRunner.query(
      `ALTER TABLE "transport_services" ADD COLUMN IF NOT EXISTS "return_to_address" character varying(500)`,
    );
    await queryRunner.query(
      `ALTER TABLE "transport_services" ADD COLUMN IF NOT EXISTS "return_from_gps_latitude" character varying(20)`,
    );
    await queryRunner.query(
      `ALTER TABLE "transport_services" ADD COLUMN IF NOT EXISTS "return_from_gps_longitude" character varying(20)`,
    );
    await queryRunner.query(
      `ALTER TABLE "transport_services" ADD COLUMN IF NOT EXISTS "return_to_gps_latitude" character varying(20)`,
    );
    await queryRunner.query(
      `ALTER TABLE "transport_services" ADD COLUMN IF NOT EXISTS "return_to_gps_longitude" character varying(20)`,
    );
    await queryRunner.query(
      `ALTER TABLE "transport_services" ADD COLUMN IF NOT EXISTS "is_airport_service" boolean NOT NULL DEFAULT false`,
    );
    await queryRunner.query(
      `ALTER TABLE "transport_services" ADD COLUMN IF NOT EXISTS "return_duration_mins" integer`,
    );

    // ── activities ─────────────────────────────────────────────
    await queryRunner.query(
      `ALTER TABLE "activities" ADD COLUMN IF NOT EXISTS "category" character varying(100)`,
    );
    await queryRunner.query(
      `ALTER TABLE "activities" ADD COLUMN IF NOT EXISTS "maintenance_periods" jsonb`,
    );
    await queryRunner.query(
      `ALTER TABLE "activities" ADD COLUMN IF NOT EXISTS "cover_image" character varying(500)`,
    );
    await queryRunner.query(`ALTER TABLE "activities" ADD COLUMN IF NOT EXISTS "gallery" jsonb`);
    await queryRunner.query(
      `ALTER TABLE "activities" ADD COLUMN IF NOT EXISTS "cutoff_type" character varying(20)`,
    );
    await queryRunner.query(
      `ALTER TABLE "activities" ADD COLUMN IF NOT EXISTS "cutoff_value" character varying(20)`,
    );
    await queryRunner.query(
      `ALTER TABLE "activities" ADD COLUMN IF NOT EXISTS "short_code" character varying(50)`,
    );
    await queryRunner.query(
      `ALTER TABLE "activities" ADD COLUMN IF NOT EXISTS "has_transfer" boolean NOT NULL DEFAULT false`,
    );

    // ── vehicles ───────────────────────────────────────────────
    await queryRunner.query(`ALTER TABLE "vehicles" ADD COLUMN IF NOT EXISTS "capping" integer`);

    // ── seasons ────────────────────────────────────────────────
    await queryRunner.query(`ALTER TABLE "seasons" ADD COLUMN IF NOT EXISTS "durations" jsonb`);

    // ── transport_pricing — vehicle_type FK + mode ────────────
    // Initial schema has cab_type NOT NULL; current entities replaced
    // it with a vehicle_type_id FK and a mode discriminator. Drop the
    // NOT NULL on cab_type so new rows save without it; keep the
    // column itself for historical data.
    await queryRunner.query(
      `ALTER TABLE "transport_pricing" ADD COLUMN IF NOT EXISTS "mode" character varying(8) NOT NULL DEFAULT 'PVT'`,
    );
    await queryRunner.query(
      `ALTER TABLE "transport_pricing" ADD COLUMN IF NOT EXISTS "vehicle_type_id" uuid`,
    );
    await queryRunner.query(`
      DO $$ BEGIN
        IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='transport_pricing' AND column_name='cab_type' AND is_nullable='NO') THEN
          ALTER TABLE "transport_pricing" ALTER COLUMN "cab_type" DROP NOT NULL;
        END IF;
      END $$;
    `);
    await queryRunner.query(`
      DO $$ BEGIN
        IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'IDX_transport_pricing_pvt_unique') THEN
          CREATE UNIQUE INDEX IF NOT EXISTS "IDX_transport_pricing_pvt_unique" ON "transport_pricing" ("tenant_id", "transport_service_id", "vehicle_type_id", "mode", "currency_id") WHERE (is_deleted = false AND mode = 'PVT');
        END IF;
      END $$;
    `);
    await queryRunner.query(
      `CREATE UNIQUE INDEX IF NOT EXISTS "IDX_transport_pricing_sic_unique" ON "transport_pricing" ("tenant_id", "transport_service_id", "mode", "currency_id") WHERE (is_deleted = false AND mode = 'SIC' AND vehicle_type_id IS NULL)`,
    );
    // FK after column exists. Wrapped in DO so a re-run doesn't error.
    await queryRunner.query(`
      DO $$ BEGIN
        IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'FK_00f8b3a90c24b5eab9114f83c05') THEN
          ALTER TABLE "transport_pricing" ADD CONSTRAINT "FK_00f8b3a90c24b5eab9114f83c05" FOREIGN KEY ("vehicle_type_id") REFERENCES "vehicle_types"("id");
        END IF;
      END $$;
    `);

    // ── vehicle_types ──────────────────────────────────────────
    await queryRunner.query(
      `ALTER TABLE "vehicle_types" ADD COLUMN IF NOT EXISTS "seating_capacity" integer`,
    );
    await queryRunner.query(
      `ALTER TABLE "vehicle_types" ADD COLUMN IF NOT EXISTS "operational_cap" integer`,
    );

    // ── query_quote_transports — share/link/vehicle_type ──────
    await queryRunner.query(
      `ALTER TABLE "query_quote_transports" ADD COLUMN IF NOT EXISTS "is_shared" boolean NOT NULL DEFAULT false`,
    );
    await queryRunner.query(
      `ALTER TABLE "query_quote_transports" ADD COLUMN IF NOT EXISTS "linked_activity_id" uuid`,
    );
    await queryRunner.query(
      `ALTER TABLE "query_quote_transports" ADD COLUMN IF NOT EXISTS "vehicle_type_id" uuid`,
    );
    // Initial schema has a vehicle_type varchar NOT NULL. Drop the
    // NOT NULL so rows can be inserted with vehicle_type_id only.
    await queryRunner.query(`
      DO $$ BEGIN
        IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='query_quote_transports' AND column_name='vehicle_type' AND is_nullable='NO') THEN
          ALTER TABLE "query_quote_transports" ALTER COLUMN "vehicle_type" DROP NOT NULL;
        END IF;
      END $$;
    `);

    // ── queries — sales team + tag arrays ─────────────────────
    await queryRunner.query(
      `ALTER TABLE "queries" ADD COLUMN IF NOT EXISTS "sales_team" jsonb NOT NULL DEFAULT '[]'::jsonb`,
    );
    await queryRunner.query(`ALTER TABLE "queries" ADD COLUMN IF NOT EXISTS "source_tags" jsonb`);
    await queryRunner.query(`ALTER TABLE "queries" ADD COLUMN IF NOT EXISTS "remark_tags" jsonb`);
    // Initial schema has assigned_to NOT NULL but the entity now uses
    // sales_team JSONB instead. Drop the NOT NULL so saves without an
    // explicit assigned_to don't fail.
    await queryRunner.query(`
      DO $$ BEGIN
        IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='queries' AND column_name='assigned_to' AND is_nullable='NO') THEN
          ALTER TABLE "queries" ALTER COLUMN "assigned_to" DROP NOT NULL;
        END IF;
      END $$;
    `);
  }

  public async down(): Promise<void> {
    // No-op down — this migration backfills missing columns. Reversing
    // it would drop application-level data. For a real rollback, drop
    // the columns manually in reverse order.
  }
}
