# typed: false
require "rails_helper"

RSpec.describe Domain::Fa::Job::ScanFileJob do
  include PerformJobHelpers

  let(:post_file) { create(:domain_post_file, :has_url) }
  let(:http_client_mock) { instance_double("::Scraper::HttpClient") }

  before do
    Scraper::ClientFactory.http_client_mock = http_client_mock
    @log_entries =
      HttpClientMockHelpers.init_http_client_mock(
        http_client_mock,
        client_mock_config,
      )
  end

  describe "#perform" do
    context "with post_file arg" do
      context "with 200 response" do
        let(:client_mock_config) do
          [
            {
              uri: post_file.url_str,
              status_code: 200,
              content_type: "image/jpeg",
              contents: "fake image data",
            },
          ]
        end

        it "processes a file successfully" do
          post_file.update!(state: "pending")
          perform_now({ post_file: post_file })

          post_file.reload
          expect(post_file.state).to eq("ok")
          expect(post_file.retry_count).to eq(0)
        end
      end

      context "with 404 response" do
        let(:client_mock_config) do
          [
            {
              uri: post_file.url_str,
              status_code: 404,
              content_type: "text/html",
              contents: "not found",
            },
          ]
        end

        it "handles 404 response" do
          post_file.update!(state: "pending")
          perform_now({ post_file: post_file })

          post_file.reload
          expect(post_file.state).to eq("terminal_error")
          expect(post_file.retry_count).to eq(0)
        end
      end

      context "with 500 response" do
        let(:client_mock_config) do
          [
            {
              uri: post_file.url_str,
              status_code: 500,
              content_type: "text/html",
              contents: "server error",
            },
          ]
        end

        it "handles other error responses" do
          post_file.update!(state: "pending")
          perform_now(
            { post_file: post_file },
            should_raise: /will retry later/,
          )

          post_file.reload
          expect(post_file.state).to eq("retryable_error")
          expect(post_file.retry_count).to eq(1)
        end
      end

      context "with existing state" do
        let(:client_mock_config) { [] }

        it "skips processing when state is ok" do
          post_file.log_entry = create(:http_log_entry)
          post_file.state_ok!
          post_file.save!

          perform_now({ post_file: post_file })

          post_file.reload
          expect(post_file.state).to eq("ok")
        end

        it "skips processing when state is terminal_error" do
          post_file.update!(state: "terminal_error")

          perform_now({ post_file: post_file })

          post_file.reload
          expect(post_file.state).to eq("terminal_error")
        end

        it "skips processing when retry_count >= 3 for retryable_error" do
          post_file.update!(state: "retryable_error", retry_count: 3)

          perform_now({ post_file: post_file })

          post_file.reload
          expect(post_file.state).to eq("terminal_error")
          expect(post_file.retry_count).to eq(3)
        end
      end
    end

    context "with post arg" do
      context "with valid post file" do
        let(:client_mock_config) do
          [
            {
              uri: post_file.url_str,
              status_code: 200,
              content_type: "image/jpeg",
              contents: "fake image data",
            },
          ]
        end

        it "processes using the post's file" do
          post_file.update!(state: "pending")
          perform_now({ post: post_file.post })

          post_file.reload
          expect(post_file.state).to eq("ok")
        end
      end

      context "with missing post" do
        let(:client_mock_config) { [] }

        it "enqueues a scan post job when post not found using fa_id" do
          perform_now({ fa_id: "12345" })

          expect(
            SpecUtil.enqueued_job_args(Domain::Fa::Job::ScanPostJob),
          ).to include(hash_including(fa_id: "12345"))
        end
      end
    end

    context "error handling" do
      let(:client_mock_config) { [] }

      it "raises error for invalid post type" do
        invalid_post = create(:domain_post_e621_post)

        perform_now(
          { post: invalid_post },
          should_raise: /invalid post model: Domain::Post::E621Post/,
        )
      end

      it "marks the file as terminal error when it has no url" do
        post_file.update!(url_str: nil, state: "pending")
        perform_now({ post_file: post_file })
        post_file.reload
        expect(post_file.state).to eq("terminal_error")
      end
    end
  end
end
