import {
  MigrationInterface,
  QueryRunner,
  Table,
  TableIndex,
  TableForeignKey,
  TableColumn,
} from 'typeorm';

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

  public async up(queryRunner: QueryRunner): Promise<void> {
    // ── 1. Add `code` column to existing tables ───────────────────────────

    if (!(await queryRunner.hasColumn('freight.service_types', 'code'))) {
      await queryRunner.addColumn(
        'freight.service_types',
        new TableColumn({
          name: 'code',
          type: 'varchar',
          length: '50',
          isNullable: true,
        }),
      );
    }
    await queryRunner.query(
      `UPDATE freight.service_types SET code = upper(replace(service_name, ' ', '_')) WHERE code IS NULL OR code = ''`,
    );
    await queryRunner.query(
      `UPDATE freight.service_types SET code = 'SERVICE_' || substring(id::text, 1, 8) WHERE code IS NULL`,
    );
    await queryRunner.query(
      `ALTER TABLE freight.service_types ALTER COLUMN code SET NOT NULL`,
    );
    const serviceTypesCodeIdx = await queryRunner.query(
      `SELECT 1 FROM pg_indexes WHERE schemaname = 'freight' AND indexname = 'idx_service_types_code' LIMIT 1`,
    );
    if (serviceTypesCodeIdx.length === 0) {
      await queryRunner.createIndex(
        'freight.service_types',
        new TableIndex({ name: 'IDX_service_types_code', columnNames: ['code'], isUnique: true }),
      );
    }

    if (!(await queryRunner.hasColumn('freight.cargo_types', 'code'))) {
      await queryRunner.addColumn(
        'freight.cargo_types',
        new TableColumn({
          name: 'code',
          type: 'varchar',
          length: '50',
          isNullable: true,
        }),
      );
    }
    await queryRunner.query(
      `UPDATE freight.cargo_types SET code = upper(replace(cargo_type_name, ' ', '_')) WHERE code IS NULL OR code = ''`,
    );
    await queryRunner.query(
      `UPDATE freight.cargo_types SET code = 'CARGO_' || substring(id::text, 1, 8) WHERE code IS NULL`,
    );
    await queryRunner.query(
      `ALTER TABLE freight.cargo_types ALTER COLUMN code SET NOT NULL`,
    );
    const cargoTypesCodeIdx = await queryRunner.query(
      `SELECT 1 FROM pg_indexes WHERE schemaname = 'freight' AND indexname = 'idx_cargo_types_code' LIMIT 1`,
    );
    if (cargoTypesCodeIdx.length === 0) {
      await queryRunner.createIndex(
        'freight.cargo_types',
        new TableIndex({ name: 'IDX_cargo_types_code', columnNames: ['code'], isUnique: true }),
      );
    }

    // ── 2. surcharge_types ────────────────────────────────────────────────

    if (!(await queryRunner.hasTable('freight.surcharge_types'))) await queryRunner.createTable(
      new Table({
        name: 'surcharge_types',
        schema: 'freight',
        columns: [
          { name: 'id', type: 'uuid', isPrimary: true, generationStrategy: 'uuid', default: 'uuid_generate_v4()' },
          { name: 'code', type: 'varchar', length: '50', isNullable: false },
          { name: 'name', type: 'varchar', length: '100', isNullable: false },
          { name: 'description', type: 'text', isNullable: true },
          { name: 'is_active', type: 'boolean', default: true },
          { name: 'created_at', type: 'timestamptz', default: 'now()' },
          { name: 'updated_at', type: 'timestamptz', default: 'now()' },
          { name: 'deleted_at', type: 'timestamptz', isNullable: true },
        ],
      }),
      true,
    );
    await queryRunner.createIndex(
      'freight.surcharge_types',
      new TableIndex({ name: 'IDX_surcharge_types_code', columnNames: ['code'], isUnique: true }),
    );
    await queryRunner.createIndex(
      'freight.surcharge_types',
      new TableIndex({ name: 'IDX_surcharge_types_is_active', columnNames: ['is_active'] }),
    );

    // ── 3. surcharges ─────────────────────────────────────────────────────

    if (!(await queryRunner.hasTable('freight.surcharges'))) await queryRunner.createTable(
      new Table({
        name: 'surcharges',
        schema: 'freight',
        columns: [
          { name: 'id', type: 'uuid', isPrimary: true, generationStrategy: 'uuid', default: 'uuid_generate_v4()' },
          { name: 'surcharge_type_id', type: 'uuid', isNullable: false },
          { name: 'fee_name', type: 'varchar', length: '255', isNullable: false },
          { name: 'trigger_description', type: 'text', isNullable: true },
          {
            name: 'calculation_method',
            type: 'enum',
            enum: ['PER_TON', 'FLAT_FEE', 'PERCENTAGE'],
            default: `'PER_TON'`,
          },
          { name: 'rate', type: 'numeric', precision: 10, scale: 2, isNullable: false },
          { name: 'currency', type: 'char', length: '3', default: `'USD'` },
          { name: 'apply_to_rail', type: 'boolean', default: false },
          { name: 'apply_to_first_mile', type: 'boolean', default: false },
          { name: 'apply_to_last_mile', type: 'boolean', default: false },
          { name: 'is_active', type: 'boolean', default: true },
          { name: 'created_at', type: 'timestamptz', default: 'now()' },
          { name: 'updated_at', type: 'timestamptz', default: 'now()' },
          { name: 'deleted_at', type: 'timestamptz', isNullable: true },
        ],
      }),
      true,
    );
    await queryRunner.createForeignKey(
      'freight.surcharges',
      new TableForeignKey({
        name: 'FK_surcharges_surcharge_type',
        columnNames: ['surcharge_type_id'],
        referencedTableName: 'freight.surcharge_types',
        referencedColumnNames: ['id'],
        onDelete: 'RESTRICT',
      }),
    );
    await queryRunner.createIndex(
      'freight.surcharges',
      new TableIndex({ name: 'IDX_surcharges_surcharge_type_id', columnNames: ['surcharge_type_id'] }),
    );
    await queryRunner.createIndex(
      'freight.surcharges',
      new TableIndex({ name: 'IDX_surcharges_is_active', columnNames: ['is_active'] }),
    );

    // ── 4. container_types ────────────────────────────────────────────────

    if (!(await queryRunner.hasTable('freight.container_types'))) await queryRunner.createTable(
      new Table({
        name: 'container_types',
        schema: 'freight',
        columns: [
          { name: 'id', type: 'uuid', isPrimary: true, generationStrategy: 'uuid', default: 'uuid_generate_v4()' },
          { name: 'size_code', type: 'varchar', length: '20', isNullable: false },
          { name: 'description', type: 'varchar', length: '100', isNullable: true },
          { name: 'containers_per_wagon', type: 'int', isNullable: false },
          { name: 'is_active', type: 'boolean', default: true },
          { name: 'created_at', type: 'timestamptz', default: 'now()' },
          { name: 'updated_at', type: 'timestamptz', default: 'now()' },
          { name: 'deleted_at', type: 'timestamptz', isNullable: true },
        ],
      }),
      true,
    );
    await queryRunner.createIndex(
      'freight.container_types',
      new TableIndex({ name: 'IDX_container_types_size_code', columnNames: ['size_code'], isUnique: true }),
    );
    await queryRunner.createIndex(
      'freight.container_types',
      new TableIndex({ name: 'IDX_container_types_is_active', columnNames: ['is_active'] }),
    );

    // ── 5. weight_limit_rules ─────────────────────────────────────────────

    if (!(await queryRunner.hasTable('freight.weight_limit_rules'))) await queryRunner.createTable(
      new Table({
        name: 'weight_limit_rules',
        schema: 'freight',
        columns: [
          { name: 'id', type: 'uuid', isPrimary: true, generationStrategy: 'uuid', default: 'uuid_generate_v4()' },
          { name: 'container_type_id', type: 'uuid', isNullable: false },
          {
            name: 'trade_direction',
            type: 'enum',
            enum: ['IMPORT', 'EXPORT', 'BOTH'],
            isNullable: false,
          },
          { name: 'max_weight_tons', type: 'numeric', precision: 10, scale: 2, isNullable: false },
          { name: 'warning_threshold_tons', type: 'numeric', precision: 10, scale: 2, isNullable: false },
          {
            name: 'exceeded_action',
            type: 'enum',
            enum: ['WARNING_ONLY', 'HARD_BLOCK'],
            default: `'WARNING_ONLY'`,
          },
          { name: 'surcharge_id', type: 'uuid', isNullable: true },
          { name: 'is_active', type: 'boolean', default: true },
          { name: 'created_at', type: 'timestamptz', default: 'now()' },
          { name: 'updated_at', type: 'timestamptz', default: 'now()' },
          { name: 'deleted_at', type: 'timestamptz', isNullable: true },
        ],
      }),
      true,
    );
    await queryRunner.createForeignKey(
      'freight.weight_limit_rules',
      new TableForeignKey({
        name: 'FK_weight_limit_rules_container_type',
        columnNames: ['container_type_id'],
        referencedTableName: 'freight.container_types',
        referencedColumnNames: ['id'],
        onDelete: 'RESTRICT',
      }),
    );
    await queryRunner.createForeignKey(
      'freight.weight_limit_rules',
      new TableForeignKey({
        name: 'FK_weight_limit_rules_surcharge',
        columnNames: ['surcharge_id'],
        referencedTableName: 'freight.surcharges',
        referencedColumnNames: ['id'],
        onDelete: 'SET NULL',
      }),
    );
    await queryRunner.createIndex(
      'freight.weight_limit_rules',
      new TableIndex({ name: 'IDX_weight_limit_rules_container_type_id', columnNames: ['container_type_id'] }),
    );
    await queryRunner.createIndex(
      'freight.weight_limit_rules',
      new TableIndex({ name: 'IDX_weight_limit_rules_surcharge_id', columnNames: ['surcharge_id'] }),
    );
    await queryRunner.createIndex(
      'freight.weight_limit_rules',
      new TableIndex({ name: 'IDX_weight_limit_rules_is_active', columnNames: ['is_active'] }),
    );

    // ── 6. priority_rules ─────────────────────────────────────────────────

    if (!(await queryRunner.hasTable('freight.priority_rules'))) await queryRunner.createTable(
      new Table({
        name: 'priority_rules',
        schema: 'freight',
        columns: [
          { name: 'id', type: 'uuid', isPrimary: true, generationStrategy: 'uuid', default: 'uuid_generate_v4()' },
          {
            name: 'priority_type',
            type: 'enum',
            enum: ['USD_PAYER', 'RAIL_AND_FORWARDING', 'GOVERNMENT_ACCOUNT', 'HIGH_VOLUME_SHIPMENT'],
            isNullable: false,
          },
          { name: 'rule_name', type: 'varchar', length: '255', isNullable: false },
          { name: 'description', type: 'text', isNullable: true },
          { name: 'activation_condition', type: 'text', isNullable: true },
          { name: 'bonus_points', type: 'int', default: 0 },
          { name: 'is_active', type: 'boolean', default: false },
          { name: 'created_at', type: 'timestamptz', default: 'now()' },
          { name: 'updated_at', type: 'timestamptz', default: 'now()' },
          { name: 'deleted_at', type: 'timestamptz', isNullable: true },
        ],
      }),
      true,
    );
    await queryRunner.createIndex(
      'freight.priority_rules',
      new TableIndex({ name: 'IDX_priority_rules_priority_type', columnNames: ['priority_type'], isUnique: true }),
    );
    await queryRunner.createIndex(
      'freight.priority_rules',
      new TableIndex({ name: 'IDX_priority_rules_is_active', columnNames: ['is_active'] }),
    );
  }

  public async down(queryRunner: QueryRunner): Promise<void> {
    await queryRunner.dropTable('freight.priority_rules', true);
    await queryRunner.dropTable('freight.weight_limit_rules', true);
    await queryRunner.dropTable('freight.container_types', true);
    await queryRunner.dropTable('freight.surcharges', true);
    await queryRunner.dropTable('freight.surcharge_types', true);
    await queryRunner.dropIndex('freight.cargo_types', 'IDX_cargo_types_code');
    await queryRunner.dropColumn('freight.cargo_types', 'code');
    await queryRunner.dropIndex('freight.service_types', 'IDX_service_types_code');
    await queryRunner.dropColumn('freight.service_types', 'code');
  }
}
