# typed: strict
class Scraper::InkbunnyHttpClientConfig < Scraper::HttpClientConfig
  DEFAULT_ALLOWED_DOMAINS = %w[inkbunny.net *.ib.metapix.net]

  sig { void }
  def initialize
    @sid = T.let(nil, T.nilable(String))
  end

  sig { override.params(performer: Scraper::CurlHttpPerformer).void }
  def do_login(performer)
    sid_model =
      GlobalState.find_or_create_by(key: "inkbunny-sid") do |gs|
        gs.value_type = :string
      end
    sid_model.with_lock do
      if sid_model.value.blank?
        sid_model.value = do_ib_login(performer)
        sid_model.save!
      end
      @sid = sid_model.value
    end
  end

  sig { override.params(uri: Addressable::URI).returns(Addressable::URI) }
  def map_uri(uri)
    raise("no sid") unless @sid
    return uri if uri.host&.end_with?("ib.metapix.net")

    new_query_ar =
      Addressable::URI.form_unencode(uri.query || "") << ["sid", @sid]
    uri = uri.dup
    uri.query = URI.encode_www_form(new_query_ar).gsub("%2C", ",")
    uri
  end

  sig { override.params(uri: Addressable::URI).returns(Addressable::URI) }
  def scrub_stored_uri(uri)
    if uri.path == "/api_login.php"
      uri = uri.dup
      new_query_ar =
        URI
          .decode_www_form(uri.query || "")
          .map do |k, v|
            %w[username password].include?(k) ? [k, "*****"] : [k, v]
          end
      uri.query = URI.encode_www_form(new_query_ar)
    end
    uri
  end

  sig { override.returns(T.nilable(T::Array[T.untyped])) }
  def cookies
    nil
  end

  sig { override.returns(T::Array[[String, Numeric]]) }
  def ratelimit
    [["inkbunny.net", 2], ["*.ib.metapix.net", 0.25]]
  end

  sig { override.returns(T::Array[String]) }
  def allowed_domains
    DEFAULT_ALLOWED_DOMAINS
  end

  sig { override.returns(Integer) }
  def redirect_limit
    2
  end

  private

  sig { params(performer: Scraper::CurlHttpPerformer).returns(String) }
  def do_ib_login(performer)
    username =
      GlobalState.find_by(key: "inkbunny-username")&.value ||
        raise("missing inkbunny username in global state")
    password =
      GlobalState.find_by(key: "inkbunny-password")&.value ||
        raise("missing inkbunny password in global state")
    uri =
      Addressable::URI.parse(
        "https://inkbunny.net/api_login.php?username=#{username}&password=#{password}",
      )
    logger.info("logging in to inkbunny as #{username}...")
    response =
      performer.do_request(
        Request.new(
          http_method: Scraper::CurlHttpPerformer::Method::Post,
          uri: uri,
          request_headers: {
          },
        ),
      )
    if response.response_code != 200
      message =
        "inkbunny login failed: #{response.response_code} #{response.body}"
      logger.error(message)
      raise message
    end
    sid =
      JSON.parse(response.body)["sid"] ||
        raise(
          "inkbunny login failed: no sid in response (#{response.body[0..1000]})",
        )
    logger.info("logged in to inkbunny as #{username}: #{sid}")
    sid
  end
end
