require "google/protobuf/well_known_types"

module VagrantPlugins
  module CommandServe
    module Service
      module CapabilityPlatformService

        def self.included(klass)
          klass.class_eval do
            attr_reader :capabilities, :default_args
          end
        end

        def initialize_capability_platform!(capabilities, default_args)
          @capabilities = capabilities
          @default_args = default_args
        end

        def has_capability_spec(*_)
          funcspec(
            args: [
              SDK::Args::NamedCapability,
            ],
            result: SDK::Platform::Capability::CheckResp,
          )
        end

        def has_capability(req, ctx)
          with_info(ctx, broker: broker) do |info|
            cap_name = mapper.funcspec_map(req)
            plugin_name = info.plugin_name
            logger.debug("checking for #{cap_name.inspect} capability in #{plugin_name}")

            caps_registry = @capabilities[plugin_name.to_sym]
            has_cap = caps_registry.key?(cap_name.to_sym)

            SDK::Platform::Capability::CheckResp.new(
              has_capability: has_cap
            )
          end
        end

        def capability_spec(req, ctx)
          funcspec(
            args: default_args.values + [
              SDK::Args::Direct
            ],
            result: SDK::Platform::Capability::Resp,
          )
        end

        def capability(req, ctx)
          with_info(ctx, broker: broker) do |info|
            cap_name = req.name.to_sym
            plugin_name = info.plugin_name.to_sym

            logger.debug("executing capability #{cap_name} on plugin #{plugin_name}")

            caps_registry = capabilities[plugin_name]
            target_cap = caps_registry.get(cap_name)

            if target_cap.nil?
              raise Vagrant::Errors::CapabilityNotFound,
                cap:  cap_name.inspect,
                host: plugin_name
            end

            args = mapper.funcspec_map(
              req.func_args,
              expect: default_args.keys + [Type::Direct]
            )

            args = capability_arguments(args)
            cap_method = target_cap.method(cap_name)

            result = cap_method.call(*args)

            logger.debug("got result #{result}")
            if result.nil? || (result.respond_to?(:empty?) && result.empty?)
              logger.debug("got empty result, returning empty response")
              return SDK::Platform::Capability::Resp.new(
                result: nil
              )
            end

            val = mapper.map(result, to: Google::Protobuf::Any)
            SDK::Platform::Capability::Resp.new(
              result: val
            )
          end
        end

        def capability_arguments(args)
          direct = args.pop
          args + direct.arguments
        end
      end
    end
  end
end
