# frozen_string_literal: true

class DiscourseAi::Completions::NovaMessageProcessor
  class NovaToolCall
    attr_reader :name, :raw_json, :id

    def initialize(name, id, partial_tool_calls: false)
      @name = name
      @id = id
      @raw_json = +""
      @tool_call = DiscourseAi::Completions::ToolCall.new(id: id, name: name, parameters: {})
      @streaming_parser =
        DiscourseAi::Completions::ToolCallProgressTracker.new(self) if partial_tool_calls
    end

    def append(json)
      @raw_json << json
      @streaming_parser << json if @streaming_parser
    end

    def notify_progress(key, value)
      @tool_call.partial = true
      @tool_call.parameters[key.to_sym] = value
      @has_new_data = true
    end

    def has_partial?
      @has_new_data
    end

    def partial_tool_call
      @has_new_data = false
      @tool_call
    end

    def to_tool_call
      parameters = JSON.parse(raw_json, symbolize_names: true)
      # we dupe to avoid poisoning the original tool call
      @tool_call = @tool_call.dup
      @tool_call.partial = false
      @tool_call.parameters = parameters
      @tool_call
    end
  end

  attr_reader :tool_calls, :input_tokens, :output_tokens

  def initialize(streaming_mode:, partial_tool_calls: false)
    @streaming_mode = streaming_mode
    @tool_calls = []
    @current_tool_call = nil
    @partial_tool_calls = partial_tool_calls
  end

  def to_tool_calls
    @tool_calls.map { |tool_call| tool_call.to_tool_call }
  end

  def process_streamed_message(parsed)
    return if !parsed

    result = nil

    if tool_start = parsed.dig(:contentBlockStart, :start, :toolUse)
      @current_tool_call = NovaToolCall.new(tool_start[:name], tool_start[:toolUseId])
    end

    if tool_progress = parsed.dig(:contentBlockDelta, :delta, :toolUse, :input)
      @current_tool_call.append(tool_progress)
    end

    result = @current_tool_call.to_tool_call if parsed[:contentBlockStop] && @current_tool_call

    if metadata = parsed[:metadata]
      @input_tokens = metadata.dig(:usage, :inputTokens)
      @output_tokens = metadata.dig(:usage, :outputTokens)
    end

    result || parsed.dig(:contentBlockDelta, :delta, :text)
  end

  def process_message(payload)
    result = []
    parsed = payload
    parsed = JSON.parse(payload, symbolize_names: true) if payload.is_a?(String)

    result << parsed.dig(:output, :message, :content, 0, :text)

    @input_tokens = parsed.dig(:usage, :inputTokens)
    @output_tokens = parsed.dig(:usage, :outputTokens)

    result
  end
end
