/*
 * swmod_api.c: Abstraction API to interact with host/lxc
 *
 * Copyright (C) 2021-2023 IOPSYS Software Solutions AB. All rights reserved.
 *
 * Author: Vivek Dutta <vivek.dutta@iopsys.eu>
 *
 * See LICENSE file for license related information.
 */

/*
* Description: This file provide abstraction api to get the information from
* from host or lxc sub-system
*/

#include "opkg_utils.h"
#include "swmod_opkg.h"
#include "swmod_host.h"
#include "tools.h"  // run_cmd

#ifdef SWMOD_LXC
#include "swmod_lxc.h"
#endif

#ifdef SWMOD_CRUN
#include "swmod_crun.h"
#endif

int swmod_run_cmd(ExecEnv_t *ee, char *cmd, char *output, int out_len)
{
	/* Get Config from package_name.list */
	int ret = 0;

	if (cmd == NULL || ee->exists == false) {
		PRINT_ERR("Either cmd is null or ee is disabled");
		return -1;
	}

	switch(ee->ee_type) {
		case EE_TYPE_HOST:
		{
			ret = run_cmd(cmd, output, out_len);
		}
		break;
		case EE_TYPE_LXC:
		{
#ifdef SWMOD_LXC
			ret = lxc_run_cmd(ee, cmd, output, out_len);
#endif
		}
		break;
		default:
		{
			PRINT_ERR("Invalid ee type %d", ee->ee_type);
		}
	}

	return ret;
}

int swmod_install_package(ExecEnv_t *env, PkgInfo *pkg, char *output, int outlen)
{
	int ret = -1;

	if (output == NULL) {
		PRINT_ERR("output buffer is NULL");
		return ret;
	}

	if (pkg == NULL) {
		snprintf(output, outlen, "Internal Error");
		return ret;
	}

	if (env->exists == false) {
		snprintf(output, outlen, "ExecEnv not exists");
		return ret;
	}

	PRINT_INFO("Installing [%s] in[%s]", pkg->url, env->name);

	switch (env->ee_type) {
	case EE_TYPE_HOST:
#ifdef SWMOD_CRUN
		ret = swmod_crun_perform_install(pkg, output, outlen);
#endif
		break;
	case EE_TYPE_LXC:
#ifdef SWMOD_LXC
		ret = swmod_lxc_install_update_remove_package(pkg, output, outlen, env->name, SWMOD_INSTALL);
#else
		ret = -1;
#endif
		break;
	default:
		ret = -1;
	}

	return ret;
}

int swmod_update_package(ExecEnv_t *env, PkgInfo *pkg, char *output, int outlen)
{
	int ret = -1;

	if (env == NULL || output == NULL) {
		PRINT_ERR("No environment or output buffer");
		return ret;
	}

	if (pkg == NULL) {
		snprintf(output, outlen, "Internal Error");
		return ret;
	}

	if (env->exists == false) {
		snprintf(output, outlen, "ExecEnv not exists");
		return ret;
	}

	PRINT_INFO("update [%s] in[%s]", pkg->url, env->name);
	switch (env->ee_type) {
	case EE_TYPE_HOST:
#ifdef SWMOD_CRUN
		snprintf(output, outlen, "DU update not supported on Host system");
#endif
		break;
	case EE_TYPE_LXC:
#ifdef SWMOD_LXC
		ret = swmod_lxc_install_update_remove_package(pkg, output, outlen, env->name, SWMOD_UPDATE);
#else
		ret = -1;
#endif
		break;
	default:
		ret = -1;
	}

	return ret;
}

int swmod_remove_package(ExecEnv_t *env, const PkgInfo *pkg, char *output, int outlen)
{
	int ret = -1;

	if (env == NULL || output == NULL) {
		PRINT_ERR("No environment or output buffer");
		return ret;
	}

	if (pkg == NULL) {
		snprintf(output, outlen, "Internal Error");
		return ret;
	}

	if (env->exists == false) {
		snprintf(output, outlen, "ExecEnv not exists");
		return ret;
	}

	PRINT_INFO("remove [%s] from[%s]", pkg->url, env->name);
	switch (env->ee_type) {
	case EE_TYPE_HOST:
#ifdef SWMOD_CRUN
		ret = swmod_crun_perform_remove(pkg->url, output, outlen);
#endif
		break;
	case EE_TYPE_LXC:
#ifdef SWMOD_LXC
		ret = swmod_lxc_install_update_remove_package(pkg, output, outlen, env->name, SWMOD_REMOVE);
#else
		ret = -1;
#endif
		break;
	default:
		ret = -1;
	}

	return ret;
}

void swmod_change_ee_state(ExecEnv_t *env, char *state, struct blob_buf *bb)
{
	if (bb == NULL)
		return;
	if (env == NULL || state == NULL) {
		blobmsg_add_u8(bb, "status", false);
		blobmsg_add_string(bb, "reason", "Insufficient information");
		return;
	}

	if (env->exists == false) {
		blobmsg_add_u8(bb, "status", false);
		blobmsg_add_string(bb, "reason", "ExecEnv not exists");
		return;
	}

	switch (env->ee_type) {
	case EE_TYPE_HOST:
	{
		blobmsg_add_u8(bb, "status", false);
		blobmsg_add_string(bb, "reason", "request can not be performed on host system");
		break;
	}
	case EE_TYPE_LXC:
	{
#ifdef SWMOD_LXC
		int err;

		PRINT_INFO("changing state of [%s] to [%s]", env->name, state);
		err = swmod_set_lxc_ee_state(env, state, swmod_config.lxc_bundle_root);
		blobmsg_add_u8(bb, "status", (err == 0) ? true : false);
		blobmsg_add_string(bb, "reason", ee_state_failure_reason(err));
#else
		blobmsg_add_u8(bb, "status", false);
		blobmsg_add_string(bb, "reason", "LXC service not present");
#endif
		break;
	}
	default:
		break;
	}

	return;
}

void populate_execution_unit(ExecEnv_t *env)
{
	if (env == NULL)
		return;

	if (env->exists == false)
		return;

	INIT_LIST_HEAD(&env->eu_list);
	switch (env->ee_type) {
	case EE_TYPE_HOST:
		/* Host system */
#ifdef SWMOD_CRUN
		populate_crun_eu(env, swmod_config.oci_bundle_root);
#endif
		break;
	case EE_TYPE_LXC:
		/* Linux containers */
#ifdef SWMOD_LXC
		populate_lxc_eu(env, swmod_config.lxc_bundle_root);
#endif
		break;
	default:
		break;
	}
}

void swmod_get_pid_detail(ExecEnv_t *env, int pid, char *cmdline, int cmd_len, int *vsize)
{
	if (env == NULL || cmdline == NULL || vsize == NULL)
		return;

	switch (env->ee_type) {
	case EE_TYPE_LXC:
#ifdef SWMOD_LXC
		get_pid_details_lxc(env, pid, cmdline, cmd_len, vsize);
#endif
		break;
	case EE_TYPE_HOST:
		break;
	default:
		break;
	}
}

void swmod_set_service_state(ExecEnv_t *env, char *name, bool state, struct blob_buf *bb)
{
	if (bb == NULL) {
		PRINT_ERR("Status buffer is null");
		return;
	}

	if (env == NULL) {
		PRINT_ERR("No environment info received");
		return;
	}

	if (env->exists == false) {
		PRINT_ERR("No environment exists");
		return;
	}

	if (name == NULL) {
		PRINT_ERR("No eu name found");
		return;
	}

	int err = -1;
	switch (env->ee_type) {
	case EE_TYPE_HOST:
#ifdef SWMOD_CRUN
		err = swmod_set_crun_service_state(env, name, state, swmod_config.oci_bundle_root);
#endif
		break;
	case EE_TYPE_LXC:
#ifdef SWMOD_LXC
		err = swmod_set_lxc_service_state(env, name, state, swmod_config.lxc_bundle_root);
#endif
		break;
	default:
		break;
	}

	if (err != 0) {
		blobmsg_add_u8(bb, "status", false);
		blobmsg_add_string(bb, "reason", "Internal Error");
	} else {
		blobmsg_add_u8(bb, "status", true);
		blobmsg_add_string(bb, "reason", "");
	}

	return;
}

void swmod_get_env_info(ExecEnv_t *env)
{
	if (env == NULL)
		return;

	switch (env->ee_type) {
	case EE_TYPE_LXC:
#ifdef SWMOD_LXC
		get_lxc_environment_info(env, swmod_config.lxc_bundle_root);
#endif
		break;
	case EE_TYPE_HOST:
		get_host_system_info(env, swmod_config.oci_bundle_root);
		break;
	default:
		break;
	}
}

void swmod_set_ee_alias(ExecEnv_t *env, const char *alias, struct blob_buf *bb)
{

	if (bb == NULL)
		return;

	if (env == NULL || alias == NULL) {
		blobmsg_add_u8(bb, "status", false);
		blobmsg_add_string(bb, "reason", "Insufficient information");
		return;
	}

	if (env->exists == false) {
		blobmsg_add_u8(bb, "status", false);
		blobmsg_add_string(bb, "reason", "ExecEnv not exists");
		return;
	}

	switch (env->ee_type) {
	case EE_TYPE_HOST:
		blobmsg_add_u8(bb, "status", false);
		blobmsg_add_string(bb, "reason", "Set alias for Host is not allowed");
		break;
	case EE_TYPE_LXC:
	{
#ifdef SWMOD_LXC
		int ret = set_lxc_alias(env->name, alias, swmod_config.lxc_bundle_root);
		if (ret != 0) {
			blobmsg_add_u8(bb, "status", false);
			blobmsg_add_string(bb, "reason", "ExecEnv is unknown");
		} else {
			blobmsg_add_u8(bb, "status", true);
			blobmsg_add_string(bb, "reason", "");
		}
#else
		blobmsg_add_u8(bb, "status", false);
		blobmsg_add_string(bb, "reason", "ExecEnv is unknown");
#endif
	}
		break;
	default:
		blobmsg_add_u8(bb, "status", false);
		blobmsg_add_string(bb, "reason", "ExecEnv is unknown");
	}

	return;
}

void swmod_set_du_alias(ExecEnv_t *env, const char *uuid, const char *alias, struct blob_buf *bb)
{
	int ret = -1;

	if (bb == NULL)
		return;

	if (env == NULL || alias == NULL || uuid == NULL) {
		blobmsg_add_u8(bb, "status", false);
		blobmsg_add_string(bb, "reason", "Insufficient information");
		return;
	}

	if (env->exists == false) {
		blobmsg_add_u8(bb, "status", false);
		blobmsg_add_string(bb, "reason", "ExecEnv not exists");
		return;
	}

	switch (env->ee_type) {
	case EE_TYPE_HOST:
#ifdef SWMOD_CRUN
		ret = set_du_alias_to_config(env->name, uuid, alias,
					swmod_config.oci_bundle_root, SWMOD_OCI_DU_UCI);
#endif
		break;
	case EE_TYPE_LXC:
#ifdef SWMOD_LXC
		ret = set_du_alias_to_config(env->name, uuid, alias,
					swmod_config.lxc_bundle_root, SWMOD_LXC_DU_UCI);
#endif
		break;
	default:
		PRINT_ERR("Unknown ENV type");
	}

	if (ret != 0) {
		blobmsg_add_u8(bb, "status", false);
		blobmsg_add_string(bb, "reason", "DU is unknown");
	} else {
		blobmsg_add_u8(bb, "status", true);
		blobmsg_add_string(bb, "reason", "");
	}

	return;
}

void swmod_set_eu_alias(ExecEnv_t *env, const char *eu_name, const char *alias, struct blob_buf *bb)
{
	int ret = -1;

	if (bb == NULL)
		return;

	if (env == NULL || alias == NULL || eu_name == NULL) {
		blobmsg_add_u8(bb, "status", false);
		blobmsg_add_string(bb, "reason", "Insufficient information");
		return;
	}

	if (env->exists == false) {
		blobmsg_add_u8(bb, "status", false);
		blobmsg_add_string(bb, "reason", "ExecEnv not exists");
		return;
	}

	switch (env->ee_type) {
	case EE_TYPE_HOST:
#ifdef SWMOD_CRUN
		ret = set_eu_alias_to_config(env->name, eu_name, alias,
					swmod_config.oci_bundle_root, SWMOD_OCI_DU_UCI);
#endif
		break;
	case EE_TYPE_LXC:
#ifdef SWMOD_LXC
		ret = set_eu_alias_to_config(env->name, eu_name, alias,
					swmod_config.lxc_bundle_root, SWMOD_LXC_DU_UCI);
#endif
		break;
	default:
		PRINT_ERR("Unknown ENV type");
	}

	if (ret != 0) {
		blobmsg_add_u8(bb, "status", false);
		blobmsg_add_string(bb, "reason", "EU is unknown");
	} else {
		blobmsg_add_u8(bb, "status", true);
		blobmsg_add_string(bb, "reason", "");
	}

	return;
}

void swmod_set_eu_autostart(ExecEnv_t *env, const char *eu_name, bool enable, struct blob_buf *bb)
{
	int ret = -1;

	if (bb == NULL)
		return;

	if (env == NULL || eu_name == NULL) {
		blobmsg_add_u8(bb, "status", false);
		blobmsg_add_string(bb, "reason", "Insufficient information");
		return;
	}

	if (env->exists == false) {
		blobmsg_add_u8(bb, "status", false);
		blobmsg_add_string(bb, "reason", "ExecEnv not exists");
		return;
	}

	switch (env->ee_type) {
	case EE_TYPE_HOST:
#ifdef SWMOD_CRUN
		ret = set_eu_autostart_to_config(env->name, eu_name, enable,
					swmod_config.oci_bundle_root, SWMOD_OCI_DU_UCI);
#endif
		break;
	case EE_TYPE_LXC:
#ifdef SWMOD_LXC
		ret = set_eu_autostart_to_config(env->name, eu_name, enable,
					swmod_config.lxc_bundle_root, SWMOD_LXC_DU_UCI);
#endif
		break;
	default:
		PRINT_ERR("Unknown ENV type");
	}

	if (ret != 0) {
		blobmsg_add_u8(bb, "status", false);
		blobmsg_add_string(bb, "reason", "EU is unknown");
	} else {
		blobmsg_add_u8(bb, "status", true);
		blobmsg_add_string(bb, "reason", "");
	}

	return;
}
