# frozen_string_literal: true

FactoryBot.define do
  # in more recent rubocop versions, this will be flagd as a violation of FactoryBot/IdSequence,
  # however this is a false positive and removing it will break the tests.
  sequence(:id) { |n| n } # rubocop:disable FactoryBot/IdSequence,Lint/RedundantCopDisableDirective
  sequence(:minor) { |n| n }
  sequence(:sha) { SecureRandom.hex(20) }
  sequence(:created_at, aliases: %w[updated_at finished_at]) do
    Time.now.utc.iso8601
  end

  factory :auto_deploy_ref, class: 'ReleaseTools::AutoDeploy::Version' do
    major { 42 }
    minor { generate(:minor) }
    patch { Time.now.strftime('%Y%m%d%H%M') }
    gitlab_sha { generate(:sha) }
    omnibus_sha { generate(:sha) }

    initialize_with { new("#{major}.#{minor}.#{patch}-#{gitlab_sha[0, 11]}.#{omnibus_sha[0, 11]}") }
  end

  factory :gitlab_response, class: 'Gitlab::ObjectifiedHash' do
    skip_create

    initialize_with do
      # NOTE: API responses are in JSON format, meaning String-based keys
      new(**attributes.deep_stringify_keys)
    end
  end

  factory :comparison, aliases: [:compare], parent: :gitlab_response do
    commit { nil }
    commits { [] }
    compare_timeout { false }
    diffs { [] }
    web_url { "https://example.com/foo/bar/compare/foo...bar" }
  end

  factory :commit_ref, parent: :gitlab_response do
    type { 'branch' }
    name { 'master' }

    trait :tag do
      type { 'tag' }
      name { 'v1.2.0' }
    end
  end

  factory :commit, parent: :gitlab_response do
    id { generate(:sha) }
    title { 'foo' }
    message { 'bar' }
    short_id { id[0, 8] }
    parent_ids { [] }
    author_name { 'GitLab Release Tools Bot' }
    created_at
    authored_date { created_at }
    committed_date { created_at }
    status { 'success' }
    trailers { {} }
    last_pipeline { association(:pipeline, sha: id, status: status) }
    web_url { "https://example.com/my-group/project/-/commits/#{id}" }

    factory :release_metadata_commit do
      transient do
        version do
          ReleaseTools::ProductVersion.from_auto_deploy(association(:auto_deploy_ref)).version
        end

        auto_deploy { 'true' }
      end

      trailers do
        association(:gitlab_response, 'Product-Version': version, 'Auto-Deploy': auto_deploy.to_s)
      end
    end
  end

  factory :branch, parent: :gitlab_response do
    name { 'master' }
    merged { true }
    default { true }
    protected { true }
    developers_can_push { false }
    developers_can_merge { false }
    can_push { true }
    web_url { "https://example.com/my-group/my-project/-/tree/#{name}" }
    commit
  end

  factory :deployment, parent: :gitlab_response do
    id
    iid { id }
    ref { 'master' }
    sha
    status { 'success' }
    created_at
    updated_at { created_at }

    trait(:success)
    trait(:failed) { status { 'failed' } }
    trait(:running) { status { 'running' } }

    # Omnibus deployments are on auto-deploy tags
    factory :omnibus_deployment do
      ref { association(:auto_deploy_ref, omnibus_sha: sha) }
    end
  end

  factory :issue, parent: :gitlab_response do
    id
    iid { id }
    project_id { generate(:id) }
    state { 'opened' }
    assignees { [] }
    author { association(:user) }
    labels { [] }
    web_url { "https://example.com/foo/bar/-/issues/#{iid}" }
    title { "A title for issue #{iid}" }
    references { association(:gitlab_response, short: "##{iid}", relative: "##{iid}", full: "my-group/my-project##{iid}") }
    created_at

    trait(:closed) { state { 'closed' } }
    trait(:opened) { state { 'opened' } }
  end

  factory :cve_issue, parent: :issue do
    id
    title { "cve issue #{id}" }
    credit { 'foo' }
    cve_id { id }
    cvss_base_score { 0 }
    cvss_severity { 'None' }
    cvss_string { 'asdf' }
    valid_yaml? { true }
    vulnerability_description { "A bug." }

    trait :low_severity do
      cvss_base_score { 2 }
      cvss_severity { 'Low' }
    end

    trait :medium_severity do
      cvss_base_score { 4 }
      cvss_severity { 'Medium' }
    end

    trait :high_severity do
      cvss_base_score { 6 }
      cvss_severity { 'High' }
    end

    trait :critical_severity do
      cvss_base_score { 9 }
      cvss_severity { 'Critical' }
    end

    trait :invalid_yaml do
      valid_yaml? { false }
    end
  end

  factory :merge_request, parent: :gitlab_response do
    id
    iid { id }
    project_id { generate(:id) }
    state { 'opened' }
    source_branch { 'feature-branch' }
    target_branch { 'master' }
    assignees { [] }
    labels { [] }
    web_url { "https://example.com/foo/bar/-/merge_requests/#{iid}" }
    merge_when_pipeline_succeeds { false }
    first_deployed_to_production_at { nil }
    title { 'MR title' }
    description { 'MR description' }
    draft { false }

    trait(:closed) { state { 'closed' } }
    trait(:locked) { state { 'locked' } }
    trait(:merged) { state { 'merged' } }
    trait(:opened) { state { 'opened' } }

    trait(:draft) do
      title { 'DRAFT: MR title' }
      draft { true }
    end
  end

  factory :pipeline, parent: :gitlab_response do
    id
    status { 'created' }
    ref { 'new-pipeline' }
    name { 'Pipeline' }
    sha
    web_url { "https://example.com/foo/bar/-/pipelines/#{id}" }
    created_at
    updated_at { created_at }
    project_id { 130 }

    trait(:created) { status { 'created' } }
    trait(:waiting_for_resource) { status { 'waiting_for_resource' } }
    trait(:preparing) { status { 'preparing' } }
    trait(:pending) { status { 'pending' } }
    trait(:running) { status { 'running' } }
    trait(:success) do
      status { 'success' }
      detailed_status { { text: "passed", label: "passed", group: "success" } }
      finished_at
    end
    trait(:failed) do
      status { 'failed' }
      finished_at
    end
    trait(:canceled) { status { 'canceled' } }
    trait(:skipped) { status { 'skipped' } }
    trait(:manual) { status { 'manual' } }
    trait(:scheduled) { status { 'scheduled' } }
    trait(:trigger) { source { 'trigger' } }
  end

  factory :pipeline_schedule, parent: :gitlab_response do
    id
    description { 'pipeline-schedule' }
    active { true }
    last_pipeline { association(:pipeline) }

    trait(:disabled) { status { 'false' } }
  end

  factory :project, parent: :gitlab_response do
    id
    name { "Project #{id}" }
    name_with_namespace { name }
    description { name }
    path { name.downcase.underscore }
    path_with_namespace { path }
    created_at
    updated_at { created_at }
    default_branch { 'main' }

    trait :fork do
      forked_from_project { attributes_for(:project) }
    end
  end

  factory :remote_mirror, parent: :gitlab_response do
    id

    enabled { 'true' }
    keep_divergent_refs { 'true' }
    only_protected_branches { 'true' }
    update_status { 'finished' }
    url { "https://*****:*****@example.com/foo/bar.git" }

    last_error { nil }
    last_update_at { generate(:updated_at) }
    last_successful_update_at { last_update_at }
    last_update_started_at { last_update_at }

    trait(:disabled) { enabled { 'false' } }
    trait :failed do
      update_status { 'failed' }
      last_successful_update_at { nil }
      last_error do
        <<~ERROR
          Some refs have diverged and have not been updated on the remote:

          refs/heads/15-0-stable-ee
          refs/heads/master
        ERROR
      end
    end
  end

  factory :job, parent: :gitlab_response do
    id
    name { 'new-job' }
    stage { 'stage1' }
    status { 'created' }
    pipeline
    user
    created_at
    updated_at { created_at }
    web_url { "https://example.com/foo/bar/-/jobs/#{id}" }

    trait(:success) do
      status { 'success' }
      finished_at
    end

    trait(:failed) do
      status { 'failed' }
      finished_at
    end

    trait(:running) do
      status { 'running' }
    end

    trait(:manual) do
      status { 'manual' }
    end

    trait(:pending) do
      status { 'pending' }
    end

    trait(:skipped) do
      status { 'skipped' }
    end

    trait(:canceled) do
      status { 'canceled' }
    end
  end

  factory :bridge_job, parent: :gitlab_response do
    id
    name { 'new-bridge-job' }
    stage { 'stage1' }
    status { 'created' }
    pipeline
    user
    downstream_pipeline { association(:pipeline) }
    created_at
    updated_at { created_at }

    trait(:success) do
      status { 'success' }
      finished_at
    end

    trait(:running) do
      status { 'running' }
    end

    trait(:failed) do
      status { 'failed' }
      finished_at
    end
  end

  factory :user, parent: :gitlab_response do
    id
    name { 'GitLab User' }
    username { 'gitlab-user' }
  end

  factory :diff, parent: :gitlab_response do
    diff do
      <<~HEREDOC
        --- a/doc/update/5.4-to-6.0.md
        +++ b/doc/update/5.4-to-6.0.md
        @@ -71,4 +71,6 @@
        sudo -u git -H bundle exec rake migrate_keys RAILS_ENV=production
        sudo -u git -H bundle exec rake migrate_inline_notes RAILS_ENV=production
        +sudo -u git -H bundle exec rake gitlab:assets:compile RAILS_ENV=production
        +```### 6. Update config files"
      HEREDOC
    end

    new_path { 'doc/update/5.4-to-6.0.md' }
    old_path { new_path }
    a_mode { nil }
    b_mode { '100644' }
    new_file { false }
    renamed_file { false }
    deleted_file { false }
  end

  factory :component_metadata, class: Hash do
    sha
    version { sha }
    ref { 'master' }
    tag { false }

    initialize_with { attributes.transform_keys(&:to_s) }
  end

  factory :releases_metadata, class: Hash do
    omnibus_gitlab_ee { association(:component_metadata) }
    gitlab_ee { association(:component_metadata) }
    gitaly { association(:component_metadata) }
    gitlab_elasticsearch_indexer { association(:component_metadata) }
    gitlab_pages { association(:component_metadata) }
    gitlab_shell { association(:component_metadata) }
    gitlab_kas { association(:component_metadata) }
    cng_ee { association(:component_metadata) }
    gitlab_assets_tag { association(:component_metadata) }
    mailroom { association(:component_metadata, sha: nil, version: '1.2.3') }

    initialize_with do
      attributes.transform_keys do |k|
        if k == :gitlab_assets_tag || k.to_s == 'gitlab_kas'
          k.to_s
        else
          k.to_s.tr('_', '-')
        end
      end
    end
  end

  factory :product_version, class: 'ReleaseTools::ProductVersion' do
    transient do
      auto_deploy_ref do
        association(:auto_deploy_ref,
                    omnibus_sha: metadata.dig('releases', 'omnibus-gitlab-ee', 'sha'),
                    gitlab_sha: metadata.dig('releases', 'gitlab-ee', 'sha'))
      end
      version do
        ReleaseTools::ProductVersion.from_auto_deploy(auto_deploy_ref).version
      end
      security { false }
      releases { association(:releases_metadata) }
      metadata_commit_id { generate(:sha) }
      metadata do
        {
          'security' => security,
          'releases' => releases
        }
      end
    end

    after(:build) do |product_version, evaluator|
      info = ReleaseTools::ProductVersion::MetadataInfo.new(
        commit_id: evaluator.metadata_commit_id,
        content: evaluator.metadata
      )
      product_version.instance_variable_set(:@full_metadata, info)
    end

    initialize_with do
      new(version)
    end
  end

  factory :note, parent: :gitlab_response do
    id
    noteable_id { nil }
    noteable_type { 'Issue' }
    noteable_iid { noteable_id }
    type { 'DiscussionNote' }
    body { "Note text" }
    attachment { nil }
    author { association(:user) }
    created_at
    updated_at { created_at }
    system { false }
    resolvable { false }
    confidential { false }
    commands_changes { {} }
  end

  factory :security_implementation_issue, class: 'ReleaseTools::Security::ImplementationIssue' do
    skip_create

    transient do
      project_id { generate(:id) }
    end

    issue { association(:issue, project_id: project_id) }
    merge_requests { build_list(:merge_request, 4, project_id: project_id) }

    initialize_with { new(issue, merge_requests) }
  end

  factory :label_event, parent: :gitlab_response do
    id
    user { association(:user) }
    created_at
    resource_type { 'Issue' }
    resource_id { generate(:id) }
    label { association(:gitlab_response, name: 'change::in-progress', color: '#0033CC') }
    action { 'add' }

    trait :add do
      action { 'add' }
    end

    trait :remove do
      action { 'remove' }
    end

    trait :change_in_progress_label do
      label { association(:gitlab_response, name: 'change::in-progress', color: '#0033CC') }
    end

    trait :change_cancelled_label do
      label { association(:gitlab_response, name: 'change::cancelled', color: '#428BCA') }
    end
  end

  factory :tag, parent: :gitlab_response do
    name { 'tag' }
    target { 'asdf1234' }
    message { 'foo' }
    protected { true }
    commit
  end

  factory :token, parent: :gitlab_response do
    id
    name { 'a token' }
    expires_at { (Date.today + 1.year).iso8601 }
    revoked { false }
    active { true }

    trait :revoked do
      revoked { true }
      active { false }
    end

    trait :expired do
      expires_at { (Date.today - 1.day).iso8601 }
      active { false }
    end
  end
end
