# typed: false
require "rails_helper"

describe Domain::Fa::Job::UserGalleryJob do
  let(:http_client_mock) { instance_double("::Scraper::HttpClient") }
  before { Scraper::ClientFactory.http_client_mock = http_client_mock }
  before do
    @log_entries =
      HttpClientMockHelpers.init_http_client_mock(
        http_client_mock,
        client_mock_config,
        any_order: true,
      )
  end

  let(:user_url_name) { "meesh" }
  let(:user_status) { "ok" }
  let(:args) { { url_name: user_url_name, scan_folders: true } }

  shared_context "user model exists" do
    let!(:user) do
      create(
        :domain_user_fa_user,
        name: user_url_name,
        url_name: user_url_name,
        state: user_status,
      )
    end
  end

  shared_context "pages are scanned" do
    let(:client_mock_config) do
      page_mocks.map do |page_mock|
        {
          uri: page_mock[:uri],
          status_code: 200,
          content_type: "text/html",
          contents: SpecUtil.read_fixture_file(page_mock[:file]),
        }
      end
    end
  end

  shared_context "user has gallery pages" do
    let(:client_mock_config) do
      [
        {
          uri: "https://www.furaffinity.net/gallery/#{user_url_name}/",
          status_code: 200,
          content_type: "text/html",
          contents:
            SpecUtil.read_fixture_file(
              "domain/fa/gallery/gallery_page_meesh.html",
            ),
        },
      ]
    end
  end

  shared_context "user is not found on fa" do
    let(:client_mock_config) do
      [
        {
          uri:
            "https://www.furaffinity.net/gallery/#{user_url_name}/1?perpage=72",
          status_code: 200,
          content_type: "text/html",
          contents:
            SpecUtil.read_fixture_file(
              "domain/fa/gallery/not_found_user_fortuneiceskular2002.html",
            ),
        },
      ]
    end
  end

  context "when scanning a user's gallery" do
    let(:user_url_name) { "pcraxkers" }
    let(:gallery_scraps_mocks) do
      [
        {
          uri:
            "https://www.furaffinity.net/scraps/#{user_url_name}/1?perpage=72",
          file: "domain/fa/gallery/scraps_page_1_pcraxkers.html",
        },
        {
          uri:
            "https://www.furaffinity.net/gallery/#{user_url_name}/1?perpage=72",
          file: "domain/fa/gallery/gallery_page_1_pcraxkers.html",
        },
      ]
    end

    let(:other_folder_mocks) do
      [
        {
          uri:
            "https://www.furaffinity.net/gallery/#{user_url_name}/folder/1400495/KNOT-ME-OUT-COMIC/1?perpage=72",
          file:
            "domain/fa/gallery/folder_knot_me_out_comic_page_1_pcraxkers.html",
        },
        {
          uri:
            "https://www.furaffinity.net/gallery/#{user_url_name}/folder/1505828/Hazbin-Hellvua/1?perpage=72",
          file:
            "domain/fa/gallery/folder_hazbin_hellvua_1505828_page_1_pcraxkers.html",
        },
        {
          uri:
            "https://www.furaffinity.net/gallery/#{user_url_name}/folder/1510162/Hazbin-Hellvua/1?perpage=72",
          file:
            "domain/fa/gallery/folder_hazbin_hellvua_1510162_page_1_pcraxkers.html",
        },
        {
          uri:
            "https://www.furaffinity.net/gallery/#{user_url_name}/folder/1505829/Pablo/1?perpage=72",
          file: "domain/fa/gallery/folder_pablo_page_1_pcraxkers.html",
        },
      ]
    end

    let(:page_mocks) { gallery_scraps_mocks + other_folder_mocks }

    include_context "user model exists"
    include_context "pages are scanned"

    context "when user has not been scanned before" do
      it "updates the user's scanned_gallery_at timestamp" do
        expect do
          perform_now(args)
          user.reload
        end.to(
          change(user, :scanned_gallery_at).to(
            be_within(1.minute).of(Time.current),
          ),
        )
      end

      it "enqueues post scan jobs for new submissions" do
        expectations = [
          change do
            SpecUtil.enqueued_job_args(Domain::Fa::Job::ScanPostJob).size
          end.by(52),
        ].reduce(:and)

        expect { perform_now(args) }.to(expectations)
      end

      it "enqueues user avatar job" do
        expect do
          perform_now(args)
          user.reload
        end.to change(user, :avatar).from(be_nil).to(be_present)

        expect(
          SpecUtil.enqueued_job_args(Domain::Fa::Job::UserAvatarJob),
        ).to match_array(hash_including({ avatar: user.avatar }))

        expect(user.avatar.url_str).to eq(
          "https://a.furaffinity.net/1674032155/pcraxkers.gif",
        )
      end

      context "when the user has an avatar" do
        before do
          user.avatar =
            create(
              :domain_user_avatar,
              state: "ok",
              url_str: "https://a.furaffinity.net/0/pcraxkers.gif",
            )
        end

        it "creates a new avatar if the url_str is different" do
          expect do
            perform_now(args)
            user.reload
          end.to change { user.avatars.count }.by(1)
        end

        it "enqueues user avatar job and updates the url_str" do
          expect do
            perform_now(args)
            user.association(:avatar).reload
          end.to change { user.avatar.url_str }.from(
            "https://a.furaffinity.net/0/pcraxkers.gif",
          ).to("https://a.furaffinity.net/1674032155/pcraxkers.gif")
        end
      end

      it "records the first log entry" do
        perform_now(args)
        user.reload
        expect(user.last_gallery_page_log_entry).to(be_present)
      end
    end

    context "when user was recently scanned" do
      before { user.update!(scanned_gallery_at: 1.day.ago) }

      it "skips scanning if not due for gallery scan" do
        expect do
          perform_now(args)
          user.reload
        end.not_to(change(user, :scanned_gallery_at))
      end

      it "forces scan when force_scan is true" do
        expect do
          perform_now(args.merge(force_scan: true))
          user.reload
        end.to(
          change(user, :scanned_gallery_at).to(
            be_within(5.seconds).of(Time.current),
          ),
        )
      end
    end

    context "when user has submission folders" do
      let(:other_folder_mocks) { [] }
      it "skips additional folders when scan_folders is false" do
        perform_now(args.merge(scan_folders: false))
        user.reload
        expect(user.last_gallery_page_log_entry).to(be_present)
      end
    end
  end

  context "when user is not found" do
    let(:user_url_name) { "fortuneiceskular2002" }
    include_context "user model exists"
    include_context "user is not found on fa"

    it "updates user state to error" do
      expect do
        perform_now(args)
        user.reload
      end.to(change(user, :state).from("ok").to("error"))
    end
  end
end
