/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "cuttlefish/host/commands/cvd/cvd.h"

#include <iostream>
#include <string>
#include <unordered_map>
#include <vector>

#include "cuttlefish/host/commands/cvd/cli/command_request.h"
#include "cuttlefish/host/commands/cvd/cli/frontline_parser.h"
#include "cuttlefish/host/commands/cvd/cli/request_context.h"
#include "cuttlefish/host/commands/cvd/instances/instance_manager.h"
#include "cuttlefish/host/commands/cvd/instances/lock/instance_lock.h"
#include "cuttlefish/host/libs/vm_manager/host_configuration.h"
#include "cuttlefish/result/result.h"

namespace cuttlefish {

Cvd::Cvd(InstanceManager& instance_manager,
         InstanceLockFileManager& lock_file_manager)
    : instance_manager_(instance_manager),
      lock_file_manager_(lock_file_manager) {}

static Result<void> EnforceHostConfigured() {
  std::vector<vm_manager::HostConfigurationAction> actions =
      CF_EXPECT(vm_manager::ValidateHostConfiguration());
  CF_EXPECT(actions.empty(), "Run `cvd setup` to configure the host.");
  return {};
}

Result<void> Cvd::HandleCommand(
    const std::vector<std::string>& cvd_process_args,
    const std::unordered_map<std::string, std::string>& env,
    selector::SelectorOptions selector_args) {
  CommandRequest request =
      CF_EXPECT(CommandRequestBuilder()
                    .AddArguments(cvd_process_args)
                    .SetEnv(env)
                    .SetSelectorOptions(std::move(selector_args))
                    .Build());

  RequestContext context(instance_manager_, lock_file_manager_);
  CvdCommandHandler* handler = CF_EXPECT(context.Handler(request));

  if (CF_EXPECT(HasHelpFlag(request.SubcommandArguments()))) {
    std::cout << CF_EXPECT(handler->DetailedHelp(request)) << std::endl;
    return {};
  }

  if (handler->RequiresHostConfiguration()) {
    CF_EXPECT(EnforceHostConfigured());
  }

  CF_EXPECT(handler->Handle(request));

  return {};
}

Result<void> Cvd::HandleCvdCommand(
    const std::vector<std::string>& all_args,
    const std::unordered_map<std::string, std::string>& env) {
  CF_EXPECT(!all_args.empty());
  std::vector<std::string> args = all_args;
  if (args.size() == 1ul) {
    args = cvd_common::Args{"cvd", "help"};
  }
  selector::SelectorOptions selector_args = CF_EXPECT(ExtractCvdArgs(args));
  // TODO(schuffelen): Deduplicate cvd process split.
  CF_EXPECT(HandleCommand(args, env, std::move(selector_args)));
  return {};
}

}  // namespace cuttlefish
