defmodule Ibex.Inbound.Handler do
  use GenServer
  require Logger
  @initial_state %{}
  def start do
    Logger.debug("Starting Inbound...")
    GenServer.start(__MODULE__, @initial_state)
  end

  def start_link(_opt) do
    Logger.info("Starting Inbound as a supervised process...")
    GenServer.start_link(__MODULE__, @initial_state, name: __MODULE__)
  end

  def init(state) do
    {:ok, state}
  end

  def handle_info({:dispatch, fields}, state) do
    msg_code = String.to_integer(Enum.at(fields, 0))
    msg_code = Ibex.Messages.Types.Inbound.parse(msg_code)
    req_id = Enum.at(fields, 1)
    msg_body = Enum.slice(fields, 2..-1)

    case msg_code do
      :contract_data ->
        Logger.info("Received contact data: #{inspect(msg_body)}")

      :symbol_samples ->
        process_symbol_samples(Enum.slice(fields, 1..-1))

      _ ->
        Logger.info("Received unhandled message type #{msg_code}. Body was: #{inspect(msg_body)}")
    end

    {:noreply, state}
  end

  defp process_symbol_samples(fields) do
    {req_id, fields} = chomp(fields)
    {num_descriptions, fields} = chomp(fields)

    nd = Enum.at(num_descriptions, 0)
    nd = String.to_integer(nd)
    contracts = Ibex.Contracts.Inbound.SymbolSamples.extract_contracts(fields, [])
    Enum.each(contracts, fn contract ->

      Logger.info("Sending #{inspect(contract.contract.con_id)} to storage")
      if contract.contract.con_id == "" do
        nil
      else
        send(Ibex.Contracts.Storage, {:add_contract, contract})
      end

    end)
  end

  defp chomp(fields) do
    Enum.split(fields, 1)
  end
end

defmodule Ibex.Contracts.Inbound.SymbolSamples do
  require Logger
  alias Ibex.Contracts.ContractDescription, as: CD
  def extract_contracts(fields, con_descs) do

    {fields, con_descs} = extract_contract(fields, con_descs)
    case length(fields) > 0 do
      true ->
        extract_contracts(fields, con_descs)
      false ->
        con_descs
    end


  end

  def extract_contract(fields, con_descs) do
    ncdesc = Ibex.Contracts.ContractDescription.t()

    {con_id, fields} = chomp(fields)
    {symbol, fields} = chomp(fields)
    {sec_type, fields} = chomp(fields)
    {primary_exchange, fields} = chomp(fields)
    {currency, fields} = chomp(fields)
    {nDerivativeSecTypes, fields} = chomp(fields)
    nt = Enum.at(nDerivativeSecTypes, 0)

    {fields, ncdesc} =
      if nt == nil do
      {fields, CD.insert_derivative_types(ncdesc, [])}
    else
      nt = String.to_integer(nt)
      {derivs, fields} = Enum.split(fields, nt)
      {fields, CD.insert_derivative_types(ncdesc, derivs)}
    end

    {description, fields} = chomp(fields)
    {issuer_id, fields} = chomp(fields)

    ncdesc =
      CD.insert_into_contract(ncdesc, :con_id, to_string(con_id))
      |> CD.insert_into_contract(:symbol, to_string(symbol))
      |> CD.insert_into_contract(:sec_type, sec_type)
      |> CD.insert_into_contract(:primary_exchange, primary_exchange)
      |> CD.insert_into_contract(:currency, currency)
      |> CD.insert_into_contract(:description, description)
      |> CD.insert_into_contract(:issuer_id, issuer_id)

    {fields, con_descs ++ [ncdesc]}
  end

  defp chomp(fields) do
    result = Enum.split(fields, 1)
    result
  end
end