# typed: false
require "rails_helper"

describe Domain::Sofurry::Job::ScanUserJob do
  let(:http_client_mock) { instance_double("::Scraper::HttpClient") }
  before { Scraper::ClientFactory.http_client_mock = http_client_mock }

  let(:fixture_data) do
    JSON.parse(
      SpecUtil.read_fixture_file("domain/sofurry/getUserProfile/101763.json"),
    )
  end

  let(:user) do
    create(
      :domain_user_sofurry_user,
      sofurry_id: 101_763,
      username: "gewitter",
      useralias: "gewitter",
    )
  end

  describe "#perform" do
    context "when page scan is due" do
      before do
        # Set scanned_page_at to more than 1 month ago to make due? return true
        user.scanned_page_at = 2.months.ago
        user.save!
      end

      context "with successful API response by sofurry_id" do
        let!(:log_entries) do
          HttpClientMockHelpers.init_http_client_mock(
            http_client_mock,
            [
              {
                uri: "https://api2.sofurry.com/std/getUserProfile?id=101763",
                status_code: 200,
                content_type: "application/json",
                contents:
                  SpecUtil.read_fixture_file(
                    "domain/sofurry/getUserProfile/101763.json",
                  ),
              },
            ],
          )
        end

        it "updates user profile with correct data" do
          perform_now({ user: user })

          user.reload
          expect(user.sofurry_id).to eq(101_763)
          expect(user.username).to eq("gewitter")
          expect(user.useralias).to eq("gewitter")
          expect(user.profile_description).to include(
            "I just kinda like to draw porn",
          )
          expect(user.profile_species).to eq("-")
          expect(user.profile_gender).to eq("na") # 0 maps to na
          expect(user.profile_orientation).to eq("na") # 0 maps to na
          expect(user.profile_mateship_status).to eq("na") # 0 maps to na
          expect(user.profile_country).to eq("")
          expect(user.profile_city).to eq("")
          expect(user.profile_num_submissions).to eq(543)
          expect(user.profile_num_submission_views).to eq(365_435)
          expect(user.profile_num_comments_received).to eq(349)
          expect(user.profile_num_comments_posted).to eq(44)
          expect(user.profile_num_profile_views).to eq(22_456)
          expect(user.registered_at).to be_within(1.day).of(
            Time.parse("21 Jul 2010"),
          )
          expect(user.scanned_page_at).to be_within(10.seconds).of(Time.now)
        end
      end

      context "when user has 'long long ago' registration date" do
        let(:old_user_fixture) do
          fixture_data.merge("registrationDate" => "long long ago")
        end

        let!(:log_entries) do
          HttpClientMockHelpers.init_http_client_mock(
            http_client_mock,
            [
              {
                uri: "https://api2.sofurry.com/std/getUserProfile?id=101763",
                status_code: 200,
                content_type: "application/json",
                contents: old_user_fixture.to_json,
              },
            ],
          )
        end

        it "sets registered_at to epoch time" do
          perform_now({ user: user })

          user.reload
          expect(user.registered_at).to eq(Time.at(0))
        end
      end

      context "when API returns 404" do
        let!(:log_entries) do
          HttpClientMockHelpers.init_http_client_mock(
            http_client_mock,
            [
              {
                uri: "https://api2.sofurry.com/std/getUserProfile?id=101763",
                status_code: 404,
                content_type: "text/html",
                contents: "Not Found",
              },
            ],
          )
        end

        it "does not update user profile" do
          original_scanned_at = user.scanned_page_at
          perform_now({ user: user })

          user.reload
          expect(user.scanned_page_at).to eq(original_scanned_at)
        end

        it "still saves the user" do
          expect(user).to receive(:save!)
          perform_now({ user: user })
        end
      end

      context "when API returns 500 error" do
        let!(:log_entries) do
          HttpClientMockHelpers.init_http_client_mock(
            http_client_mock,
            [
              {
                uri: "https://api2.sofurry.com/std/getUserProfile?id=101763",
                status_code: 500,
                content_type: "text/html",
                contents: "Internal Server Error",
              },
            ],
          )
        end

        it "does not update user profile" do
          original_scanned_at = user.scanned_page_at
          perform_now({ user: user })

          user.reload
          expect(user.scanned_page_at).to eq(original_scanned_at)
        end
      end
    end

    context "when page scan is not due" do
      let!(:log_entries) do
        HttpClientMockHelpers.init_http_client_mock(http_client_mock, [])
      end

      before do
        # Set scanned_page_at to less than 1 month ago to make due? return false
        user.scanned_page_at = 1.day.ago
        user.save!
      end

      it "does not scan user profile" do
        expect do perform_now({ user: user }) end.not_to change {
          user.scanned_page_at
        }
      end

      it "still saves the user" do
        expect(user).to receive(:save!)
        perform_now({ user: user })
      end
    end

    context "when the user does not exist" do
      before do
        HttpClientMockHelpers.init_http_client_mock(
          http_client_mock,
          [
            {
              uri: "https://api2.sofurry.com/std/getUserProfile?id=783918234",
              status_code: 200,
              content_type: "application/json",
              contents:
                File.read(
                  Rails.root.join(
                    "test/fixtures/files/domain/sofurry/getUserProfile/783918234.user_not_found.json",
                  ),
                ),
            },
          ],
        )
      end
      it "creates the user and marks in an error state" do
        expect do perform_now({ sofurry_id: 783_918_234 }) end.to change {
          Domain::User::SofurryUser.find_by(sofurry_id: 783_918_234)&.state
        }.from(nil).to("error")
      end

      it "sets the last_scan_log_entry" do
        perform_now({ sofurry_id: 783_918_234 })
        expect(
          Domain::User::SofurryUser.find_by(
            sofurry_id: 783_918_234,
          )&.last_scan_log_entry,
        ).to be_present
      end
    end
  end
end
