class Captain::Tools::BaseTool < RubyLLM::Tool
  attr_accessor :assistant, :conversation

  FALLBACK_EXCLUDED_TOOL_KEYS = %w[faq_lookup react_to_message].freeze

  def initialize(assistant, user: nil, conversation: nil)
    @assistant = assistant
    @user = user
    @conversation = conversation
    super()
  end

  def call(args)
    RubyLLM.logger.debug "Tool #{name} called with: #{args.inspect}"
    result = execute(**args.transform_keys(&:to_sym))
    RubyLLM.logger.debug "Tool #{name} returned: #{result.inspect}"

    return result if result.is_a?(RubyLLM::Tool::Halt)
    return result if fallback_message.blank?
    return result unless errorish_result?(result)

    fallback_message
  rescue StandardError => e
    fallback = fallback_message
    return fallback if fallback.present?

    raise e
  end

  def execute(*args, **params)
    # Default implementation to be overridden
  end

  def parameters
    schema = respond_to?(:tool_parameters_schema) ? tool_parameters_schema : nil
    return build_parameters_from_schema(schema) if schema.is_a?(Hash) && schema[:properties].present?

    super
  end

  protected

  def resolve_params(args, params)
    # RubyLLM: [params_hash], {}
    # Agents: [context], {params_hash}
    actual_params = if args.first.is_a?(Hash) && params.empty?
                      args.first
                    else
                      params
                    end
    actual_params.with_indifferent_access
  end

  def resolve_context(tool_context)
    if tool_context.respond_to?(:state)
      tool_context.state
    elsif tool_context.is_a?(Hash)
      tool_context
    else
      {}
    end.with_indifferent_access
  end

  def active?
    true
  end

  def build_parameters_from_schema(schema)
    required = Array(schema[:required]).map(&:to_s)

    schema[:properties].each_with_object({}) do |(name, spec), acc|
      spec = spec.with_indifferent_access
      acc[name.to_s] = RubyLLM::Parameter.new(
        name.to_s,
        type: spec[:type] || 'string',
        desc: spec[:description],
        required: required.include?(name.to_s)
      )
    end
  end

  def fallback_message
    return nil if FALLBACK_EXCLUDED_TOOL_KEYS.include?(name)
    return nil unless @assistant&.account_id && @assistant&.id

    config = Captain::ToolConfig.find_by(
      account_id: @assistant.account_id,
      captain_assistant_id: @assistant.id,
      tool_key: name
    )
    config&.fallback_message.presence
  end

  def errorish_result?(result)
    return false unless result.is_a?(String)

    text = result.strip.downcase
    text.start_with?('erro', 'erro:', 'system info', 'falha', 'failure')
  end

  private

  def user_has_permission(permission)
    return false if @user.blank?

    account_user = AccountUser.find_by(account_id: @assistant.account_id, user_id: @user.id)
    return false if account_user.blank?

    return account_user.custom_role.permissions.include?(permission) if account_user.custom_role.present?

    account_user.administrator? || account_user.agent?
  end
end
