# frozen_string_literal: true

# Helper module for dealing with the migration from samples
# to custom repositories. We need to query with SQL because probably we will not
# have the "Sample" and other related models at the time this code will execute

module Tasks
  module SamplesToRepositoryMigrationService
    include ActiveRecord::Sanitization::ClassMethods

    def self.prepare_repository(team, copy_num = 0)
      repository = Repository.new(
        name: copy_num > 0 ? "Samples (#{copy_num})" : 'Samples',
        team: team,
        created_by: team.created_by
      )
      return repository if repository.save
      prepare_repository(team, copy_num + 1)
    end

    def self.prepare_text_value_custom_columns(team, repository)
      custom_columns_sql = <<-SQL
        SELECT * FROM custom_fields WHERE team_id = #{team.id}
      SQL
      # execute query
      custom_columns = ActiveRecord::Base.connection.execute(custom_columns_sql)

      repository_columns = []
      custom_columns.each_with_index do |column, index|
        repository_column = RepositoryColumn.create!(
          repository: repository,
          created_by_id: column.fetch('user_id') { repository.created_by_id },
          data_type: :RepositoryTextValue,
          name: column.fetch('name') { "Custom column (#{index})" },
          created_at: column.fetch('created_at') { DateTime.now },
          updated_at: column.fetch('updated_at') { DateTime.now }
        )
        repository_columns << repository_column
      end
      repository_columns
    end

    def self.prepare_list_value_custom_columns_with_list_items(team, repository)
      conn = ActiveRecord::Base.connection
      sample_types_sql = <<-SQL
        SELECT name, created_by_id, last_modified_by_id
        FROM sample_types
        WHERE team_id = #{team.id}
      SQL
      sample_groups_sql = <<-SQL
        SELECT name, created_by_id, last_modified_by_id
        FROM sample_groups
        WHERE team_id = #{team.id}
      SQL
      # execute query
      sample_types = conn.execute(sample_types_sql)
      sample_groups = conn.execute(sample_groups_sql)

      sample_group = RepositoryColumn.create!(
        repository: repository,
        created_by_id: repository.created_by_id,
        data_type: :RepositoryListValue,
        name: 'Sample group'
      )

      # needs some random string to prevent duplications error
      sample_group_color = RepositoryColumn.create!(
        repository: repository,
        created_by_id: repository.created_by_id,
        data_type: :RepositoryTextValue,
        name: 'Sample group color hex'
      )

      sample_type = RepositoryColumn.create!(
        repository: repository,
        created_by_id: repository.created_by_id,
        data_type: :RepositoryListValue,
        name: 'Sample type'
      )

      sample_groups.each_with_index do |item, index|
        created_by = item['created_by_id'] || team.created_by_id
        last_modified_by = item['last_modified_by_id'] || team.created_by_id
        timestamp = conn.quote(Time.now.to_s(:db))
        values = [
          sample_group.id,
          conn.quote(item.fetch('name') { "sample group item (#{index})" }),
          created_by,
          last_modified_by,
          timestamp,
          timestamp
        ]
        list_item_sql = <<-SQL
          INSERT INTO repository_list_items
            (repository_column_id,
             data,
             created_by_id,
             last_modified_by_id,
             created_at, updated_at)
          VALUES (#{values.join(', ')})
        SQL
        conn.execute(list_item_sql)
      end

      sample_types.each_with_index do |item, index|
        created_by = item['created_by_id'] || team.created_by_id
        last_modified_by = item['last_modified_by_id'] || team.created_by_id
        timestamp = conn.quote(Time.now.to_s(:db))
        values = [
          sample_type.id,
          conn.quote(item.fetch('name') { "sample type item (#{index})" }),
          created_by,
          last_modified_by,
          timestamp,
          timestamp
        ]
        list_item_sql = <<-SQL
          INSERT INTO repository_list_items
            (repository_column_id,
             data,
             created_by_id,
             last_modified_by_id,
             created_at, updated_at)
          VALUES (#{values.join(', ')})
        SQL
        conn.execute(list_item_sql)
      end

      [sample_group, sample_type, sample_group_color]
    end

    def self.get_sample_custom_fields(sample_id)
      custom_sample_fields_sql = <<-SQL
        SELECT custom_fields.name AS column_name_reference,
               sample_custom_fields.value,
               sample_custom_fields.created_at,
               sample_custom_fields.updated_at
        FROM custom_fields
        INNER JOIN sample_custom_fields
        ON custom_fields.id = sample_custom_fields.custom_field_id
        WHERE sample_custom_fields.sample_id = #{sample_id}
      SQL
      ActiveRecord::Base.connection.execute(custom_sample_fields_sql).to_a
    end

    def self.get_assigned_sample_module(sample_id)
      assigned_samples_sql = <<-SQL
        SELECT my_module_id, assigned_by_id
        FROM sample_my_modules
        WHERE sample_my_modules.sample_id = #{sample_id}
      SQL
      ActiveRecord::Base.connection.execute(assigned_samples_sql).to_a
    end

    def self.fetch_all_team_samples(team)
      samples_sql = <<-SQL
        SELECT samples.id AS sample_id,
               samples.name AS sample_name,
               samples.user_id AS sample_created_by_id,
               samples.last_modified_by_id AS sample_last_modified_by_id,
               samples.created_at AS sample_created_at,
               samples.updated_at AS sample_updated_at,
               sample_types.name AS sample_type_name,
               sample_groups.name AS sample_group_name,
               sample_groups.color AS sample_group_color
        FROM samples
        LEFT OUTER JOIN sample_types
        ON samples.sample_type_id = sample_types.id
        LEFT OUTER JOIN sample_groups
        ON samples.sample_group_id = sample_groups.id
        WHERE samples.team_id = #{team.id}
        ORDER BY samples.id
      SQL

      ActiveRecord::Base.connection.execute(samples_sql).to_a
    end

    def self.get_custom_columns(team, repository)
      prepare_text_value_custom_columns(team, repository) +
        prepare_list_value_custom_columns_with_list_items(team, repository)
    end

    def self.create_text_cell(row_id, column_id, data,
                              created_by_id, last_modified_by_id)
      conn = ActiveRecord::Base.connection
      timestamp = conn.quote(Time.now.to_s(:db))
      values = [conn.quote(data), created_by_id, last_modified_by_id, timestamp, timestamp]
      value_sql = <<-SQL
        INSERT INTO repository_text_values
          (data, created_by_id, last_modified_by_id,
           created_at, updated_at)
        VALUES (#{values.join(', ')})
        RETURNING id
      SQL

      value_id = conn.execute(value_sql)[0]['id']

      values = [row_id, column_id, value_id, conn.quote('RepositoryTextValue'),
                timestamp, timestamp]
      cell_sql = <<-SQL
        INSERT INTO repository_cells
          (repository_row_id, repository_column_id, value_id, value_type,
           created_at, updated_at)
        VALUES (#{values.join(', ')})
      SQL
      conn.execute(cell_sql)
    end

    def self.create_list_cell(row_id, column_id, list_item_id,
                              created_by_id, last_modified_by_id)
      conn = ActiveRecord::Base.connection
      timestamp = conn.quote(Time.now.to_s(:db))
      values = [list_item_id, created_by_id, last_modified_by_id,
                timestamp, timestamp]
      list_value_sql = <<-SQL
        INSERT INTO repository_list_values
          (repository_list_item_id, created_by_id, last_modified_by_id,
           created_at, updated_at)
        VALUES (#{values.join(', ')})
        RETURNING id
      SQL
      value_id = ActiveRecord::Base.connection.execute(list_value_sql)[0]['id']

      values = [row_id, column_id, value_id, conn.quote('RepositoryListValue'),
                timestamp, timestamp]
      cell_sql = <<-SQL
        INSERT INTO repository_cells
          (repository_row_id, repository_column_id, value_id, value_type,
           created_at, updated_at)
        VALUES (#{values.join(', ')})
      SQL
      ActiveRecord::Base.connection.execute(cell_sql)
    end

    def self.update_smart_annotations(team, mappings)
      team.projects.eager_load(:project_comments).each do |pr|
        pr.project_comments.each do |comment|
          comment.save! if update_annotation(comment.message, mappings)
        end
        pr.experiments.each do |exp|
          exp.save! if update_annotation(exp.description, mappings)
          exp.my_modules.eager_load(:task_comments).each do |task|
            task.task_comments.each do |comment|
              comment.save! if update_annotation(comment.message, mappings)
            end
            task.save! if update_annotation(task.description, mappings)
            task.protocol.steps.eager_load(:step_comments).each do |step|
              step.step_comments.each do |comment|
                comment.save! if update_annotation(comment.message, mappings)
              end
              step.save! if update_annotation(step.description, mappings)
            end
            task.results.eager_load(:result_comments, :result_text).each do |res|
              res.result_comments.each do |comment|
                comment.save! if update_annotation(comment.message, mappings)
              end
              next unless res.result_text
              res.save! if update_annotation(res.result_text.text, mappings)
            end
          end
        end
      end
      team.protocols.where(my_module: nil).each do |protocol|
        protocol.steps.eager_load(:step_comments).each do |step|
          step.step_comments.each do |comment|
            comment.save! if update_annotation(comment.message, mappings)
          end
          step.save! if update_annotation(step.description, mappings)
        end
      end
      team.repositories.each do |rep|
        rep.repository_rows.includes(repository_cells: :repository_text_value)
           .where('repository_cells.value_type': 'RepositoryTextValue')
           .each do |row|
          row.repository_cells.each do |cell|
            if update_annotation(cell.repository_text_value.data, mappings)
              cell.repository_text_value.save!
            end
          end
        end
      end
    end

    # Returns true if text was updated
    def self.update_annotation(text, sample_mappings)
      return false if text.nil?
      updated = false
      text.scan(/~sam~\w+\]/).each do |text_match|
        orig_id_encoded = text_match.match(/~sam~(\w+)\]/)[1]
        orig_id = orig_id_encoded.base62_decode
        next unless sample_mappings[orig_id]
        new_id_encoded = sample_mappings[orig_id].base62_encode
        text.sub!("~sam~#{orig_id_encoded}]", "~rep_item~#{new_id_encoded}]")
        updated = true
      end
      updated
    end
  end
end