defmodule Grazer.UI do
  use GenServer

  def start_link(arg) do
    GenServer.start_link(__MODULE__, arg)
  end

  def update(p, arg) do
    GenServer.cast(p, arg)
  end

  def info(p) when is_binary(p) do
    GenServer.call(p, :info)
  end

  @impl true
  def init(_init_arg) do
    port = Port.open({:spawn, path()}, [:binary])
    ref = Port.monitor(port)
    {:ok, [port: port, ref: ref]}
  end

  @impl true
  def handle_call(:info, _from, state) do
    {:reply, {:ok, {Port.info(state[:port])}}, state}
  end

  @impl true
  def handle_cast(msg, state) do
    send(state[:port], {self(), {:command, msg <> "\n"}})
    {:noreply, state}
  end

  @impl true
  def handle_info({:DOWN, ref, :port, port, :normal}, state) do
    IO.puts("down: #{inspect({ref, port})}")
    ^port = state[:port]
    ^ref = state[:ref]
    {:stop, :normal, state}
  end

  @impl true
  def handle_info([resp: {_status, _headers, body}], state) do
    # naively assuming content type is html
    {:ok, essense} = Grazer.HTML.distill(body)
    {:ok, json} = JSON.encode(essense)
    update(self(), json)
    {:noreply, state}
  end

  @impl true
  def handle_info({port, {:data, data}}, state) do
    IO.puts("data: #{inspect({port, data, state})}")
    ^port = state[:port]
    case String.split(data, " ") do
      ["navigate" | rest] -> Grazer.Net.request([
          get: String.trim(Enum.join(rest, " ")),
          for: self(),
      ])
    end
    {:noreply, state}
  end

  @impl true
  def handle_info(msg, state) do
    IO.puts("info: #{inspect({msg, state})}")
    {:noreply, state}
  end

  @impl true
  def terminate(_reason, state) do
    send(state[:port], {self(), :close})
    :ok
  end

  defp path do
    "gui/target/debug/gui"
    "gui/target/release/gui"
  end
end