require "test_helper"

class HeartbeatImportRunnerTest < ActiveSupport::TestCase
  include ActiveJob::TestHelper

  setup do
    ActionMailer::Base.deliveries.clear
    @original_queue_adapter = ActiveJob::Base.queue_adapter
    ActiveJob::Base.queue_adapter = :test
  end

  teardown do
    ActionMailer::Base.deliveries.clear
    clear_enqueued_jobs
    ActiveJob::Base.queue_adapter = @original_queue_adapter
  end

  test "run_import emails the user when a wakatime import succeeds" do
    user = User.create!(timezone: "UTC")
    user.email_addresses.create!(email: "success-#{SecureRandom.hex(4)}@example.com", source: :signing_in)
    run = user.heartbeat_import_runs.create!(
      source_kind: :wakatime_dump,
      state: :queued,
      encrypted_api_key: "secret"
    )

    file = Tempfile.new([ "heartbeats", ".json" ])
    file.write(
      {
        heartbeats: [
          {
            entity: "/tmp/test.rb",
            type: "file",
            time: 1_700_000_000.0,
            project: "hackatime",
            language: "Ruby",
            is_write: true
          }
        ]
      }.to_json
    )
    file.close

    HeartbeatImportRunner.run_import(import_run_id: run.id, file_path: file.path)

    run.reload
    assert_equal "completed", run.state
    assert_nil run.encrypted_api_key
    assert_enqueued_jobs 1, only: ActionMailer::MailDeliveryJob
  ensure
    file&.unlink
  end

  test "run_import does not overwrite a completed run if completion follow-up fails" do
    user = User.create!(timezone: "UTC")
    user.email_addresses.create!(email: "post-complete-#{SecureRandom.hex(4)}@example.com", source: :signing_in)
    run = user.heartbeat_import_runs.create!(
      source_kind: :wakatime_dump,
      state: :queued,
      encrypted_api_key: "secret"
    )

    file = Tempfile.new([ "heartbeats", ".json" ])
    file.write(
      {
        heartbeats: [
          {
            entity: "/tmp/test.rb",
            type: "file",
            time: 1_700_000_000.0,
            project: "hackatime",
            language: "Ruby",
            is_write: true
          }
        ]
      }.to_json
    )
    file.close

    singleton_class = HeartbeatImportRunner.singleton_class
    singleton_class.alias_method :__original_send_completion_email_for_test, :send_completion_email
    singleton_class.define_method(:send_completion_email) do |_completed_run|
      raise "mail transport failed"
    end

    begin
      HeartbeatImportRunner.run_import(import_run_id: run.id, file_path: file.path)
    ensure
      singleton_class.alias_method :send_completion_email, :__original_send_completion_email_for_test
      singleton_class.remove_method :__original_send_completion_email_for_test
      file&.unlink
    end

    run.reload
    assert_equal "completed", run.state
    assert_nil run.error_message
    assert_nil run.encrypted_api_key
    assert_empty ActionMailer::Base.deliveries
  end

  test "fail_run_for_error! emails the user about invalid api keys" do
    user = User.create!(timezone: "UTC")
    user.email_addresses.create!(email: "invalid-key-#{SecureRandom.hex(4)}@example.com", source: :signing_in)
    run = user.heartbeat_import_runs.create!(
      source_kind: :wakatime_dump,
      state: :queued,
      encrypted_api_key: "secret"
    )

    HeartbeatImportRunner.fail_run_for_error!(
      import_run_id: run.id,
      error: HeartbeatImportDumpClient::AuthenticationError.new("Authentication failed (401)", status: 401)
    )

    run.reload
    assert_equal "failed", run.state
    assert_equal "WakaTime rejected the import because the API key is invalid.", run.error_message
    assert_nil run.encrypted_api_key
    assert_enqueued_jobs 1, only: ActionMailer::MailDeliveryJob
  end

  test "fail_run_for_error! includes slack guidance for hackatime v1 provider errors" do
    user = User.create!(timezone: "UTC")
    user.email_addresses.create!(email: "provider-error-#{SecureRandom.hex(4)}@example.com", source: :signing_in)
    run = user.heartbeat_import_runs.create!(
      source_kind: :hackatime_v1_dump,
      state: :queued,
      encrypted_api_key: "secret"
    )

    HeartbeatImportRunner.fail_run_for_error!(
      import_run_id: run.id,
      error: HeartbeatImportDumpClient::TransientError.new("Request failed with status 500", status: 500)
    )

    run.reload
    assert_equal "failed", run.state
    assert_equal "Hackatime v1 ran into an error while processing the import. Please reach out to #hackatime-help on Slack.", run.error_message
    assert_nil run.encrypted_api_key
    assert_enqueued_jobs 1, only: ActionMailer::MailDeliveryJob
  end

  test "start_remote_import bypasses cooldown for superadmins" do
    user = User.create!(timezone: "UTC", admin_level: :superadmin)
    Flipper.enable_actor(:imports, user)

    user.heartbeat_import_runs.create!(
      source_kind: :wakatime_dump,
      state: :completed,
      encrypted_api_key: "old-secret",
      remote_requested_at: 1.minute.ago
    )

    assert_difference -> { user.heartbeat_import_runs.count }, +1 do
      run = HeartbeatImportRunner.start_remote_import(
        user: user,
        provider: "wakatime_dump",
        api_key: "new-secret"
      )

      assert_equal "wakatime_dump", run.source_kind
      assert_equal "queued", run.state
    end
  end

  test "serialize omits cooldown for superadmins" do
    user = User.create!(timezone: "UTC", admin_level: :superadmin)
    run = user.heartbeat_import_runs.create!(
      source_kind: :wakatime_dump,
      state: :completed,
      encrypted_api_key: "secret",
      remote_requested_at: 1.minute.ago
    )

    payload = HeartbeatImportRunner.serialize(run)

    assert_nil payload[:cooldown_until]
  end

  test "refreshable_remote_run? stops once a remote import is downloading or importing" do
    %i[downloading_dump importing].each do |state|
      user = User.create!(timezone: "UTC")
      Flipper.enable_actor(:imports, user)

      run = user.heartbeat_import_runs.create!(
        source_kind: :wakatime_dump,
        state: state,
        encrypted_api_key: "secret"
      )
      run.update_column(:updated_at, 10.seconds.ago)

      assert_not HeartbeatImportRunner.refreshable_remote_run?(run)
    end
  end
end
