require "http/client"
require "json"
require "uri"

module BlueskyApi
  extend self

  APPVIEW_HOST = "public.api.bsky.app"

  struct Profile
    include JSON::Serializable

    property did : String
    property handle : String
    @[JSON::Field(key: "displayName")]
    property display_name : String?
    property avatar : String?
  end

  struct FollowsResponse
    include JSON::Serializable

    property follows : Array(Profile)
    property cursor : String?
  end

  struct SearchResponse
    include JSON::Serializable

    property actors : Array(Profile)
  end

  def get_profile(did_or_handle : String) : Profile?
    params = HTTP::Params.encode({"actor" => did_or_handle})
    uri = URI.parse("https://#{APPVIEW_HOST}/xrpc/app.bsky.actor.getProfile?#{params}")

    client = HTTP::Client.new(uri)
    client.connect_timeout = 10.seconds
    client.read_timeout = 30.seconds

    response = client.get(uri.request_target)
    client.close

    return nil unless response.status.success?
    Profile.from_json(response.body)
  rescue
    nil
  end

  def get_follows(did : String, cursor : String? = nil) : FollowsResponse?
    params = {"actor" => did, "limit" => "100"}
    params["cursor"] = cursor if cursor

    encoded = HTTP::Params.encode(params)
    uri = URI.parse("https://#{APPVIEW_HOST}/xrpc/app.bsky.graph.getFollows?#{encoded}")

    client = HTTP::Client.new(uri)
    client.connect_timeout = 10.seconds
    client.read_timeout = 30.seconds

    response = client.get(uri.request_target)
    client.close

    return nil unless response.status.success?
    FollowsResponse.from_json(response.body)
  rescue
    nil
  end

  def search_actors(query : String) : Array(Profile)
    params = HTTP::Params.encode({"q" => query, "limit" => "25"})
    uri = URI.parse("https://#{APPVIEW_HOST}/xrpc/app.bsky.actor.searchActors?#{params}")

    client = HTTP::Client.new(uri)
    client.connect_timeout = 10.seconds
    client.read_timeout = 30.seconds

    response = client.get(uri.request_target)
    client.close

    return [] of Profile unless response.status.success?
    SearchResponse.from_json(response.body).actors
  rescue
    [] of Profile
  end
end
