import { MigrationInterface, QueryRunner } from 'typeorm';

/**
 * Data backfill for the contract–booking separation (docs/new-doc.md §17).
 *
 * For every legacy `booking_type = 'GENERAL_CONTRACT'` booking we synthesise a
 * `freight.contracts` row from its contract-phase columns, copy its routes
 * (contract_route_lines → contract_routes, dropping quantity), and point the
 * contract + every child shipment booking (linked via booking_orders) at it.
 *
 * Per §19 item 1, historical ONE_TIME bookings that went through the full
 * contract flow get a contract parent inserted and `contract_id` set on the same
 * booking row (no row split).
 *
 * Idempotent: skips bookings that already have `contract_id` set, and matches a
 * synthesised contract by a deterministic `CTR-<bookingId>` reference.
 */
export class BackfillContractsFromBookings1823000000000
  implements MigrationInterface
{
  name = 'BackfillContractsFromBookings1823000000000';

  public async up(queryRunner: QueryRunner): Promise<void> {
    // 1. One contract per GENERAL_CONTRACT booking, carrying the contract-phase
    //    columns. Reference is derived from the source booking id so re-runs are
    //    idempotent (ON CONFLICT DO NOTHING on the unique reference).
    await queryRunner.query(`
      INSERT INTO freight.contracts (
        reference, company_id, company_profile_id, is_government, government_institution,
        contract_kind, trade_direction, freight_type, service_type_id, payment_currency,
        customs_clearing_enabled, customs_clearing_agent, equipment_return,
        first_mile_pickup_address, first_mile_pickup_lat, first_mile_pickup_lng,
        last_mile_delivery_address, last_mile_delivery_lat, last_mile_delivery_lng,
        is_hazardous, is_reefer, estimated_shipment_date,
        contract_validity_days, contract_valid_from, contract_valid_until, expires_at,
        status, clearance_status, clearance_cycle_number,
        pricing_breakdown, contract_type, contract_template_key, contract_generated_at,
        contract_summary, version_number,
        approved_by_staff_id, approved_by_staff_at,
        signed_by_director_id, signed_by_director_at,
        signed_by_ceo_id, signed_by_ceo_at, customer_signed_at, fully_executed_at,
        created_at, updated_at
      )
      SELECT
        'CTR-' || b.id::text, b.company_id, b.company_profile_id, b.is_government, b.government_institution,
        'GENERAL', b.trade_direction, b.freight_type, b.service_type_id, b.payment_currency,
        b.customs_clearing_enabled, b.customs_clearing_agent, b.equipment_return,
        b.first_mile_pickup_address, b.first_mile_pickup_lat, b.first_mile_pickup_lng,
        b.last_mile_delivery_address, b.last_mile_delivery_lat, b.last_mile_delivery_lng,
        b.is_hazardous, b.is_reefer, b.estimated_shipment_date,
        b.contract_validity_days, b.contract_valid_from, b.contract_valid_until, b.expires_at,
        CASE
          WHEN b.status IN ('CONTRACT_ACTIVE') THEN 'CONTRACT_ACTIVE'
          WHEN b.status IN ('CONTRACT_CLOSED') THEN 'CONTRACT_CLOSED'
          WHEN b.status IN ('EXPIRED') THEN 'EXPIRED'
          WHEN b.status IN ('CANCELLED') THEN 'CANCELLED'
          WHEN b.status IN ('REJECTED') THEN 'REJECTED'
          ELSE 'CONTRACT_ACTIVE'
        END,
        CASE WHEN b.customs_clearing_enabled THEN 'NOT_APPLICABLE' ELSE 'NOT_APPLICABLE' END,
        0,
        b.pricing_breakdown,
        b.contract_type, b.contract_template_key, b.contract_generated_at,
        b.contract_summary, COALESCE(b.version_number, 1),
        b.approved_by_staff_id, b.approved_by_staff_at,
        b.signed_by_director_id, b.signed_by_director_at,
        b.signed_by_ceo_id, b.signed_by_ceo_at, b.customer_signed_at, b.fully_executed_at,
        b.created_at, b.updated_at
      FROM freight.bookings b
      WHERE b.booking_type = 'GENERAL_CONTRACT'
      ON CONFLICT (reference) DO NOTHING;
    `);

    // 2. Copy each general contract's route lines into contract_routes (no qty).
    await queryRunner.query(`
      INSERT INTO freight.contract_routes (contract_id, origin_yard_id, destination_yard_id, km, sort_order, created_at, updated_at)
      SELECT c.id, crl.origin_yard_id, crl.destination_yard_id, crl.km, 0, now(), now()
      FROM freight.contract_route_lines crl
      JOIN freight.contracts c ON c.reference = 'CTR-' || crl.contract_booking_id::text
      ON CONFLICT (contract_id, origin_yard_id, destination_yard_id) DO NOTHING;
    `);

    // 3. Point the general-contract booking itself at its new contract, and stamp
    //    the denormalized contract_kind for the active-booking index.
    await queryRunner.query(`
      UPDATE freight.bookings b
      SET contract_id = c.id, contract_kind = 'GENERAL', created_by_role = 'CUSTOMER'
      FROM freight.contracts c
      WHERE c.reference = 'CTR-' || b.id::text
        AND b.booking_type = 'GENERAL_CONTRACT'
        AND b.contract_id IS NULL;
    `);

    // 4. Point each child shipment booking (spawned via booking_orders) at the
    //    same contract as its parent general contract.
    await queryRunner.query(`
      UPDATE freight.bookings child
      SET contract_id = c.id, contract_kind = 'GENERAL', created_by_role = 'CUSTOMER'
      FROM freight.booking_orders bo
      JOIN freight.contracts c ON c.reference = 'CTR-' || bo.contract_booking_id::text
      WHERE child.id = bo.booking_id
        AND child.contract_id IS NULL;
    `);

    // 5. Historical ONE_TIME bookings that completed the contract flow: synthesise
    //    a contract parent and point the same booking row at it (no row split).
    await queryRunner.query(`
      INSERT INTO freight.contracts (
        reference, company_id, company_profile_id, is_government, government_institution,
        contract_kind, trade_direction, freight_type, service_type_id, payment_currency,
        customs_clearing_enabled, customs_clearing_agent, equipment_return,
        first_mile_pickup_address, first_mile_pickup_lat, first_mile_pickup_lng,
        last_mile_delivery_address, last_mile_delivery_lat, last_mile_delivery_lng,
        is_hazardous, is_reefer, estimated_shipment_date,
        contract_validity_days, contract_valid_from, contract_valid_until,
        status, clearance_status, clearance_cycle_number,
        pricing_breakdown, contract_type, contract_template_key, contract_generated_at,
        contract_summary, version_number,
        approved_by_staff_id, approved_by_staff_at,
        signed_by_director_id, signed_by_director_at,
        signed_by_ceo_id, signed_by_ceo_at, customer_signed_at, fully_executed_at,
        created_at, updated_at
      )
      SELECT
        'CTR-' || b.id::text, b.company_id, b.company_profile_id, b.is_government, b.government_institution,
        'ONE_TIME', b.trade_direction, b.freight_type, b.service_type_id, b.payment_currency,
        b.customs_clearing_enabled, b.customs_clearing_agent, b.equipment_return,
        b.first_mile_pickup_address, b.first_mile_pickup_lat, b.first_mile_pickup_lng,
        b.last_mile_delivery_address, b.last_mile_delivery_lat, b.last_mile_delivery_lng,
        b.is_hazardous, b.is_reefer, b.estimated_shipment_date,
        b.contract_validity_days, b.contract_valid_from, b.contract_valid_until,
        'FULLY_EXECUTED', 'NOT_APPLICABLE', 0,
        b.pricing_breakdown, b.contract_type, b.contract_template_key, b.contract_generated_at,
        b.contract_summary, COALESCE(b.version_number, 1),
        b.approved_by_staff_id, b.approved_by_staff_at,
        b.signed_by_director_id, b.signed_by_director_at,
        b.signed_by_ceo_id, b.signed_by_ceo_at, b.customer_signed_at, b.fully_executed_at,
        b.created_at, b.updated_at
      FROM freight.bookings b
      WHERE COALESCE(b.booking_type, 'ONE_TIME') = 'ONE_TIME'
        AND b.contract_id IS NULL
        AND b.contract_generated_at IS NOT NULL
      ON CONFLICT (reference) DO NOTHING;
    `);

    await queryRunner.query(`
      UPDATE freight.bookings b
      SET contract_id = c.id, contract_kind = 'ONE_TIME', created_by_role = 'CUSTOMER'
      FROM freight.contracts c
      WHERE c.reference = 'CTR-' || b.id::text
        AND COALESCE(b.booking_type, 'ONE_TIME') = 'ONE_TIME'
        AND b.contract_id IS NULL;
    `);

    // 6. Build a single route per ONE_TIME contract from the booking's own
    //    origin/destination (general contracts already got their routes in step 2).
    await queryRunner.query(`
      INSERT INTO freight.contract_routes (contract_id, origin_yard_id, destination_yard_id, sort_order, created_at, updated_at)
      SELECT c.id, b.origin_yard_id, b.destination_yard_id, 0, now(), now()
      FROM freight.bookings b
      JOIN freight.contracts c ON c.id = b.contract_id AND c.contract_kind = 'ONE_TIME'
      WHERE b.origin_yard_id IS NOT NULL AND b.destination_yard_id IS NOT NULL
      ON CONFLICT (contract_id, origin_yard_id, destination_yard_id) DO NOTHING;
    `);
  }

  public async down(queryRunner: QueryRunner): Promise<void> {
    // Unlink bookings and drop the synthesised contracts (and their cascaded routes).
    await queryRunner.query(`
      UPDATE freight.bookings SET contract_id = NULL, contract_route_id = NULL
      WHERE contract_id IN (SELECT id FROM freight.contracts WHERE reference LIKE 'CTR-%');
    `);
    await queryRunner.query(`DELETE FROM freight.contracts WHERE reference LIKE 'CTR-%';`);
  }
}
