class ReconcileMaterialSyncSuppressions < ActiveRecord::Migration[8.0]
  MATERIAL_VERSION_EVENT_CONSTRAINT = 'material_versions_event_type_valid'

  def up
    ensure_material_sync_suppressions!
    ensure_material_source_columns!
    ensure_material_active_source_key!
    remove_material_file_suppression!
    remove_material_version_file_suppression!
    reconcile_material_version_event_constraint!
  end

  def down
    raise ActiveRecord::IrreversibleMigration
  end

  private

  def ensure_material_sync_suppressions!
    return if table_exists?(:material_sync_suppressions)

    create_table :material_sync_suppressions do |t|
      t.string :source_kind, null: false
      t.string :source_uri
      t.string :drive_path
      t.string :drive_file_id
      t.string :normalized_source_key, null: false
      t.string :reason, null: false
      t.references :created_by_user, foreign_key: { to_table: :users }
      t.timestamps

      t.index :normalized_source_key, unique: true
      t.index :source_kind
      t.index :drive_file_id
    end
  end

  def ensure_material_source_columns!
    add_column :materials, :source_kind, :string unless column_exists?(:materials, :source_kind)
    add_column :materials, :source_uri, :string unless column_exists?(:materials, :source_uri)
    add_column :materials, :source_path, :string unless column_exists?(:materials, :source_path)
    unless column_exists?(:materials, :source_file_id)
      add_column :materials, :source_file_id, :string
    end
    unless column_exists?(:materials, :normalized_source_key)
      add_column :materials, :normalized_source_key, :string
    end

    if index_exists?(:materials, :normalized_source_key)
      remove_index :materials, column: :normalized_source_key
    end
  end

  def ensure_material_active_source_key!
    unless column_exists?(:materials, :active_normalized_source_key)
      change_table :materials do |t|
        t.virtual :active_normalized_source_key,
                  type: :string,
                  as: 'IF(discarded_at IS NULL, normalized_source_key, NULL)',
                  stored: false
      end
    end

    return if index_exists?(:materials,
                            :active_normalized_source_key,
                            name: 'index_materials_on_active_normalized_source_key')

    add_index :materials,
              :active_normalized_source_key,
              unique: true,
              name: 'index_materials_on_active_normalized_source_key'
  end

  def remove_material_file_suppression!
    if foreign_key_exists?(:materials, :users, column: :file_suppressed_by_user_id)
      remove_foreign_key :materials, column: :file_suppressed_by_user_id
    end

    if index_exists?(:materials,
                    :file_suppressed_by_user_id,
                    name: 'index_materials_on_file_suppressed_by_user_id')
      remove_index :materials, name: 'index_materials_on_file_suppressed_by_user_id'
    end

    remove_column :materials, :file_suppressed_at if column_exists?(
        :materials,
        :file_suppressed_at)
    remove_column :materials, :file_suppressed_by_user_id if column_exists?(
        :materials,
        :file_suppressed_by_user_id)
    remove_column :materials, :file_suppression_reason if column_exists?(
        :materials,
        :file_suppression_reason)
  end

  def remove_material_version_file_suppression!
    remove_column :material_versions, :file_suppressed_at if column_exists?(
        :material_versions,
        :file_suppressed_at)
    remove_column :material_versions, :file_suppression_reason if column_exists?(
        :material_versions,
        :file_suppression_reason)
  end

  def reconcile_material_version_event_constraint!
    constraint = check_constraints(:material_versions).find do |candidate|
      candidate.name == MATERIAL_VERSION_EVENT_CONSTRAINT
    end
    return if constraint && !constraint.expression.include?('suppress')

    if constraint
      remove_check_constraint :material_versions, name: MATERIAL_VERSION_EVENT_CONSTRAINT
    end
    add_check_constraint :material_versions,
                         "event_type IN ('create', 'update', 'discard', 'restore')",
                         name: MATERIAL_VERSION_EVENT_CONSTRAINT
  end
end
