# frozen_string_literal: true

# Seed test groups with:
#   1. 2 Subgroups per level
#   1. 2 Users & group members per group
#   1. 2 Epics, 2 Milestones & 2 Projects per group
#   1. Project issues
#
# It also assigns each project's issue with one of group's or ascendants
# groups milestone & epic.
#
# @param subgroups_depth - number of subgroup levels
# @param username - user creating subgroups (i.e. GitLab admin)
#
# @example
#   bundle exec rake "gitlab:seed:group_seed[5, root]"
#
namespace :gitlab do
  namespace :seed do
    desc 'Seed groups with sub-groups/projects/epics/milestones for Group Import testing'
    task :group_seed, [:subgroups_depth, :username] => :gitlab_environment do |_t, args|
      require 'sidekiq/testing'

      GroupSeeder.new(
        subgroups_depth: args.subgroups_depth,
        username: args.username
      ).seed
    end
  end
end

class GroupSeeder
  PROJECT_URL = 'https://gitlab.com/gitlab-org/gitlab-test.git'

  attr_reader :all_group_ids

  def initialize(subgroups_depth:, username:)
    @subgroups_depth = subgroups_depth.to_i
    @user = User.find_by_username(username)
    @group_names = Set.new
    @resource_count = 2
    @all_groups = {}
    @all_group_ids = []
  end

  def seed
    create_groups

    puts 'Done!'
  end

  def create_groups
    create_root_group
    create_sub_groups
    create_users_and_members
    create_epics if Gitlab.ee?
    create_labels
    create_milestones

    Sidekiq::Testing.inline! do
      create_projects
    end
  end

  def create_users_and_members
    all_group_ids.each do |group_id|
      @resource_count.times do |_|
        user = create_user
        create_member(user.id, group_id)
      end
    end
  end

  def create_root_group
    root_group = ::Groups::CreateService.new(@user, group_params).execute

    track_group_id(1, root_group.id)
  end

  def create_sub_groups
    (2..@subgroups_depth).each do |level|
      parent_level = level - 1
      current_level = level
      parent_groups = @all_groups[parent_level]

      parent_groups.each do |parent_id|
        @resource_count.times do |_|
          sub_group = ::Groups::CreateService.new(@user, group_params(parent_id: parent_id)).execute

          track_group_id(current_level, sub_group.id)
        end
      end
    end
  end

  def track_group_id(depth_level, group_id)
    @all_groups[depth_level] ||= []
    @all_groups[depth_level] << group_id
    @all_group_ids << group_id
  end

  def group_params(parent_id: nil)
    name = unique_name

    {
      name: name,
      path: name,
      parent_id: parent_id
    }
  end

  def unique_name
    name = ffaker_name
    name = ffaker_name until @group_names.add?(name)
    name
  end

  def ffaker_name
    FFaker::Lorem.characters(5)
  end

  def create_user
    User.create!(
      username: FFaker::Internet.user_name,
      name: FFaker::Name.name,
      email: FFaker::Internet.email,
      confirmed_at: DateTime.now,
      password: Devise.friendly_token
    )
  end

  def create_member(user_id, group_id)
    roles = Gitlab::Access.values

    GroupMember.create(user_id: user_id, access_level: roles.sample, source_id: group_id)
  end

  def create_epics
    all_group_ids.each do |group_id|
      @resource_count.times do |_|
        group = Group.find(group_id)

        epic_params = {
          title: FFaker::Lorem.sentence(6),
          description: FFaker::Lorem.paragraphs(3).join("\n\n"),
          author: group.users.sample,
          group: group
        }

        Epic.create!(epic_params)
      end
    end
  end

  def create_labels
    all_group_ids.each do |group_id|
      @resource_count.times do |_|
        group = Group.find(group_id)
        label_title = FFaker::Product.brand

        Labels::CreateService.new(title: label_title, color: "##{Digest::MD5.hexdigest(label_title)[0..5]}").execute(group: group)
      end
    end
  end

  def create_milestones
    all_group_ids.each do |group_id|
      @resource_count.times do |i|
        group = Group.find(group_id)

        milestone_params = {
          title: "v#{i}.0",
          description: FFaker::Lorem.sentence,
          state: [:active, :closed].sample
        }

        Milestones::CreateService.new(group, group.members.sample, milestone_params).execute
      end
    end
  end

  def create_projects
    all_group_ids.each do |group_id|
      group = Group.find(group_id)

      @resource_count.times do |i|
        _, project_path = PROJECT_URL.split('/')[-2..-1]

        project_path.gsub!('.git', '')

        params = {
          import_url: PROJECT_URL,
          namespace_id: group.id,
          name: project_path.titleize + FFaker::Lorem.characters(10),
          description: FFaker::Lorem.sentence,
          visibility_level: 0,
          skip_disk_validation: true
        }

        project = nil

        Sidekiq::Worker.skipping_transaction_check do
          project = ::Projects::CreateService.new(@user, params).execute
          project.send(:_run_after_commit_queue)
          project.import_state.send(:_run_after_commit_queue)
          project.repository.expire_all_method_caches
        end

        create_project_issues(project)
        assign_issues_to_epics_and_milestones(project)
      end
    end
  end

  def create_project_issues(project)
    Gitlab::Seeder.quiet do
      seeder = Quality::Seeders::Issues.new(project: project)
      seeder.seed(backfill_weeks: 2, average_issues_per_week: 2)
    end
  end

  def assign_issues_to_epics_and_milestones(project)
    group_ids = project.group.self_and_ancestors.map(&:id)

    project.issues.each do |issue|
      issue_params = {
        milestone: Milestone.where(group: group_ids).sample
      }

      issue_params[:epic] = Epic.where(group: group_ids).sample if Gitlab.ee?

      issue.update(issue_params)
    end
  end
end