require "rails_helper"

RSpec.describe Domain::E621::Job::ScanUserFavsJob do
  let(:http_client_mock) { instance_double("::Scraper::HttpClient") }
  before { Scraper::ClientFactory.http_client_mock = http_client_mock }

  let(:user) { create(:domain_user_e621_user, e621_id: 123_456) }
  let(:job) { described_class.new }

  let!(:log_entries) do
    HttpClientMockHelpers.init_http_client_mock(
      http_client_mock,
      [
        {
          uri:
            "https://e621.net/posts.json?tags=status:any+fav:#{user.url_name}+order:id_desc&limit=4",
          content_type: "application/json",
          contents:
            File.read("test/fixtures/files/domain/e621/job/favorites_0.json"),
        },
        {
          uri:
            "https://e621.net/posts.json?tags=status:any+fav:#{user.url_name}+order:id_desc&limit=4&page=b5129881",
          content_type: "application/json",
          contents: { posts: [] }.to_json,
          caused_by_entry_idx: 0,
        },
      ],
    )
  end

  describe "#perform" do
    it "scans user favorites and creates fav records" do
      expect do perform_now({ user: user }) end.to change {
        user.scanned_favs_status
      }.from(nil).to("ok")

      # Verify the posts were created
      expect(Domain::Post::E621Post.pluck(:e621_id)).to match_array(
        [5_212_363, 5_214_461, 5_306_537, 2_518_409, 5_129_881],
      )
      expect(Domain::UserPostFav.count).to eq(5)

      post5212363 = Domain::Post::E621Post.find_by(e621_id: 5_212_363)
      expect(post5212363).to be_present
      expect(post5212363.caused_by_entry).to eq(log_entries[0])
      expect(post5212363.last_index_page).to eq(log_entries[0])
      expect(post5212363.index_page_ids).to eq([log_entries[0].id])
      # Verify StaticFileJob was enqueued for each new post
      static_file_jobs =
        SpecUtil.enqueued_job_args(Domain::E621::Job::StaticFileJob)
      expect(static_file_jobs.size).to eq(5)
      expect(
        static_file_jobs.map { |args| args[:post_file].post.e621_id },
      ).to match_array(Domain::Post::E621Post.pluck(:e621_id))

      # Verify specific post details from fixture
      post = Domain::Post::E621Post.find_by(e621_id: 5_212_363)
      expect(post).to be_present
      expect(post.file).to be_present
      expect(post.file.url_str).to eq(
        "https://static1.e621.net/data/87/18/8718995a7dd49f24dfae9ffc042c9578.png",
      )

      # Verify fav relationship
      fav = Domain::UserPostFav.find_by(user: user, post: post)
      expect(fav).to be_present
    end

    it "updates scanned_favs_at timestamp" do
      expect { perform_now({ user: user }) }.to change {
        user.reload.scanned_favs_at
      }.from(nil).to be_within(3.seconds).of(Time.now)
    end

    context "when API returns error" do
      let!(:log_entries) do
        HttpClientMockHelpers.init_http_client_mock(
          http_client_mock,
          [
            {
              method: :get,
              uri:
                "https://e621.net/posts.json?tags=status:any+fav:#{user.url_name}+order:id_desc&limit=4",
              content_type: "application/json",
              status_code: 404,
              contents: "Not Found",
            },
          ],
        )
      end

      it "handles HTTP errors appropriately" do
        expect { perform_now({ user: user }) }.to raise_error(
          /non 200 response/,
        ).and change { user.scanned_favs_status }.from(nil).to("error")
      end
    end

    context "when favorites are hidden" do
      let!(:log_entries) do
        HttpClientMockHelpers.init_http_client_mock(
          http_client_mock,
          [
            {
              method: :get,
              uri:
                "https://e621.net/posts.json?tags=status:any+fav:#{user.url_name}+order:id_desc&limit=4",
              content_type: "application/json",
              status_code: 403,
              contents:
                File.read(
                  "test/fixtures/files/domain/e621/job/favorites_hidden.json",
                ),
            },
          ],
        )
      end

      it "handles hidden favorites appropriately" do
        expect { perform_now({ user: user }) }.to change {
          user.reload.favs_are_hidden
        }.from(nil).to(true)
      end

      it "updates scanned_favs_at timestamp" do
        expect { perform_now({ user: user }) }.to change {
          user.reload.scanned_favs_at
        }.from(nil).to(be_within(3.seconds).of(Time.now))
      end

      it "sets scanned_favs_status to ok" do
        expect { perform_now({ user: user }) }.to change {
          user.reload.scanned_favs_status
        }.from(nil).to("ok")
      end

      it "does not create any favs" do
        expect { perform_now({ user: user }) }.not_to change(
          Domain::UserPostFav,
          :count,
        )
      end
    end
  end
end
