#-- encoding: UTF-8
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2017 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
# See doc/COPYRIGHT.rdoc for more details.
#++

require_relative 'migration_utils/legacy_journal_migrator'
require_relative 'migration_utils/journal_migrator_concerns'

class LegacyPlanningElementJournalData < ActiveRecord::Migration[4.2]
  include Migration::Utils

  class UnknownJournaledError < ::StandardError
  end

  class UnknownTypeError < ::StandardError
  end

  def up
    migrator.run

    reset_public_key_sequence_in_postgres 'journals'

    unless migrator.missing_type_ids.empty?
      puts 'Cannot resolve new type ids for all journals!'\
           "\n\n"\
           'The following list contains all legacy planning element '\
           'type ids for which no new type id exists. Furthermore, '\
           'the list contains all journal ids for which no new type '\
           'id exists.'\
           "\n\n"\
           "#{migrator.missing_type_ids}"\
           "\n\n"\
           "The type id is set to '0' for all journals containing a "\
           'planning element type id for which no type id exists.'\
           "\n\n\n"
    end

    unless migrator.missing_journaled_ids.empty?
      puts 'Cannot resolve work package ids for all journals!'\
           "\n\n"\
           'The following list contains all legacy planning element '\
           'ids for which no new work package id exists. Furthermore,'\
           ' the list contains all journal ids for which no new'\
           'work package id exists.'\
           "\n\n"\
           "#{migrator.missing_journaled_ids}"\
           "\n\n"\
           "The work package id is set to '0' for all journals "\
           'containing a planning element type id for which no type '\
           'id exists.'
      "\n\n\n"
    end
  end

  def down
    migrator.remove_journals_derived_from_legacy_journals 'customizable_journals',
                                                          'attachable_journals'
  end

  def migrator
    @migrator ||= Migration::LegacyJournalMigrator.new 'Timelines_PlanningElementJournal', 'work_package_journals' do
      extend Migration::JournalMigratorConcerns::Attachable
      extend Migration::JournalMigratorConcerns::Customizable

      self.journable_class = 'WorkPackage'

      def migrate(legacy_journal)
        update_journaled_id(legacy_journal)

        super
      end

      def migrate_key_value_pairs!(to_insert, legacy_journal, journal_id)
        update_type_id(to_insert, journal_id)

        set_empty_description(to_insert)

        migrate_attachments(to_insert, legacy_journal, journal_id)

        migrate_custom_values(to_insert, legacy_journal, journal_id)
      end

      def update_journaled_id(legacy_journal)
        legecy_journal_id = legacy_journal['id']
        old_journaled_id = legacy_journal['journaled_id']
        new_journaled_id = new_journaled_id_for_old(old_journaled_id)

        if new_journaled_id.nil?
          add_missing_journaled_id_for_legacy_journal_id(old_journaled_id, legecy_journal_id)

          new_journaled_id = 0
        end

        legacy_journal['journaled_id'] = new_journaled_id
      end

      def update_type_id(to_insert, journal_id)
        return if to_insert['planning_element_type_id'].nil? ||
                  to_insert['planning_element_type_id'].last.nil?

        old_type_id = to_insert['planning_element_type_id'].last

        new_type_id = new_type_id_for_old(old_type_id)

        if new_type_id.nil?
          add_missing_type_id_for_journal_id(old_type_id, journal_id)

          new_type_id = 0
        end

        to_insert['type_id'] = [nil, new_type_id]
      end

      def new_journaled_id_for_old(old_journaled_id)
        @new_journaled_ids ||= begin
          old_new = db_select_all <<-SQL
            SELECT journaled_id AS old_id, new_id
            FROM legacy_journals
            LEFT JOIN legacy_planning_elements
            ON legacy_journals.journaled_id = legacy_planning_elements.id
            WHERE type = 'Timelines_PlanningElementJournal'
          SQL

          old_new.inject({}) do |mem, entry|
            mem[entry['old_id']] = entry['new_id']
            mem
          end
        end

        @new_journaled_ids[old_journaled_id]
      end

      def new_type_id_for_old(old_type_id)
        @new_type_ids ||= begin
          old_new = db_select_all <<-SQL
            SELECT id AS old_id, new_id
            FROM legacy_planning_element_types
          SQL

          old_new.inject({}) do |mem, entry|
            # the old_type_id was casted to a fixnum
            # cheaper to change this here
            mem[entry['old_id'].to_i] = entry['new_id'].to_i
            mem
          end
        end

        @new_type_ids[old_type_id]
      end

      def set_empty_description(to_insert)
        to_insert['description'] = [nil, ''] unless to_insert.has_key?('description')
      end

      def missing_journaled_ids
        @missing_journaled_ids ||= {}
      end

      def add_missing_journaled_id_for_legacy_journal_id(old_journaled_id, legacy_journal_id)
        missing_journaled_ids[old_journaled_id] = [] unless missing_journaled_ids.has_key? old_journaled_id
        missing_journaled_ids[old_journaled_id] << legacy_journal_id
      end

      def missing_type_ids
        @missing_type_ids ||= {}
      end

      def add_missing_type_id_for_journal_id(old_type_id, journal_id)
        missing_type_ids[old_type_id] = [] unless missing_type_ids.has_key? old_type_id
        missing_type_ids[old_type_id] << journal_id
      end
    end
  end
end
