/******************************************************************
 * Copyright (c) 2025 Bocloud Technologies Co., Ltd.
 * installer is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain n copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 ******************************************************************/

package containerd

import (
	"encoding/json"
	"errors"

	"gopkg.openfuyao.cn/bkeadm/pkg/executor/exec"
	"gopkg.openfuyao.cn/bkeadm/utils"
	"gopkg.openfuyao.cn/bkeadm/utils/log"
)

type NerdContainerInfo struct {
	Id    string `json:"Id"`
	State struct {
		Status     string `json:"Status"`
		Running    bool   `json:"Running"`
		Paused     bool   `json:"Paused"`
		Restarting bool   `json:"Restarting"`
		Pid        uint   `json:"Pid"`
		ExitCode   uint   `json:"ExitCode"`
		FinishedAt string `json:"FinishedAt"`
	} `json:"State"`
	Image           string `json:"Image"`
	Name            string `json:"Name"`
	RestartCount    uint   `json:"RestartCount"`
	Platform        string `json:"Platform"`
	NetworkSettings struct {
		IPAddress  string `json:"IPAddress"`
		MacAddress string `json:"MacAddress"`
	} `json:"NetworkSettings"`
}

type NerdImageInfo struct {
	Id           string   `json:"Id"`
	RepoTags     []string `json:"RepoTags"`
	Architecture string   `json:"Architecture"`
	OS           string   `json:"Os"`
}

var cmd = exec.CommandExecutor{}

func EnsureImageExists(image string) error {
	info, err := ImageInspect(image)
	if err != nil || len(info.Id) == 0 {
		log.Infof("Image %s is downloading", image)
		res, err := cmd.ExecuteCommandWithOutput(utils.NerdCtl, "pull", image)
		if err != nil {
			log.Error(res)
			return err
		}
	}
	return nil
}

func ImageInspect(image string) (NerdImageInfo, error) {
	var info []NerdImageInfo
	result, err := cmd.ExecuteCommandWithOutput(utils.NerdCtl, "inspect", image)
	if err != nil {
		return NerdImageInfo{}, err
	}

	log.Debug(result)
	err = json.Unmarshal([]byte(result), &info)
	if err != nil {
		return NerdImageInfo{}, err
	}
	if len(info) == 1 {
		return info[0], nil
	}
	return NerdImageInfo{}, errors.New("not found")
}

func EnsureContainerRun(containerId string) (bool, error) {
	info, exist := ContainerExists(containerId)
	if exist {
		if info.State.Running {
			return true, nil
		}
		if err := ContainerRemove(containerId); err != nil {
			return false, err
		}
	}
	return false, nil
}

func ContainerExists(containerId string) (NerdContainerInfo, bool) {
	var info []NerdContainerInfo
	result, err := cmd.ExecuteCommandWithOutput(utils.NerdCtl, "inspect", containerId)
	if err != nil {
		return NerdContainerInfo{}, false
	}
	log.Debug(result)
	err = json.Unmarshal([]byte(result), &info)
	if err != nil {
		log.Errorf("Unmarshal nerdctl inspect result for %s failed: %v", containerId, err)
		return NerdContainerInfo{}, false
	}
	if len(info) == 1 {
		return info[0], true
	}
	return NerdContainerInfo{}, false
}

func ContainerInspect(containerId string) (NerdContainerInfo, error) {
	var info []NerdContainerInfo
	result, err := cmd.ExecuteCommandWithOutput(utils.NerdCtl, "inspect", containerId)
	if err != nil {
		return NerdContainerInfo{}, err
	}
	log.Debug(result)
	err = json.Unmarshal([]byte(result), &info)
	if err != nil {
		log.Errorf("Unmarshal nerdctl inspect result for %s failed: %v", containerId, err)
		return NerdContainerInfo{}, err
	}
	if len(info) == 1 {
		return info[0], nil
	}
	return NerdContainerInfo{}, errors.New("not found")
}

func ContainerRemove(containerId string) error {
	err := cmd.ExecuteCommand(utils.NerdCtl, "rm", "-f", containerId)
	if err != nil {
		return err
	}
	return nil
}

func Load(imageFile string) error {
	return cmd.ExecuteCommand(utils.NerdCtl, "load", "--input", imageFile)
}

func Run(script []string) error {
	return cmd.ExecuteCommand(utils.NerdCtl, script...)
}

func CP(src, dst string) error {
	return cmd.ExecuteCommand(utils.NerdCtl, "cp", src, dst)
}