// Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.

// 应用包删除方法
package build

import (
	"fleetmanager/api/errors"
	"fleetmanager/api/params"
	"fleetmanager/api/service/constants"
	"fleetmanager/client"
	"fleetmanager/db/dao"
	"github.com/beego/beego/v2/server/web/context"
	"strings"
)

// @Title Delete
// @Description
// @Author wangnannan 2022-05-07 09:20:38 ${time}
// @Param ctx
// @Return *errors.CodedError
func (s *Service) Delete(ctx *context.Context) *errors.CodedError {

	projectId := ctx.Input.Param(params.ProjectId)
	buildId := ctx.Input.Param(params.BuildId)

	s.Logger.Info("buildId=%s   projectId=%s  delete begin!", buildId, projectId)

	tmpBuild, e := dao.GetBuildById(buildId, projectId)
	if e != nil {
		return s.ErrorMsg(errors.ServerInternalError,
			"Delete build Failed!Search DB failed!", e)
	}
	if tmpBuild.State == constants.BuildDeleted {
		return s.ErrorMsg(errors.BuildNotExists, "This build is in deleted state", nil)
	}

	// check if build is related to any fleet
	err := s.CheckBuildInUsedSystem(buildId, projectId)
	if err != nil {
		return err
	}

	// if build created by IMS-image directly, just delete the record from db
	if tmpBuild.BuildType == constants.VMTYPE && tmpBuild.StorageBucketName != "" && tmpBuild.StorageKey != "" {
		// Delete build in IMS
		err = s.DeleteBuildInIMS(buildId, projectId)
		if err != nil {
			return err
		}
	}
	if tmpBuild.BuildType == constants.PODTYPE {
		err = s.DeleteBuildInSwr(buildId, projectId)
		if err != nil {
			return err
		}
	}

	// Delete buildId record from db
	err = s.DeleteByBuild(buildId, projectId)
	if err != nil {
		return err
	}

	s.Logger.Info("buildId=%s   projectId=%s  delete success!", buildId, projectId)
	return nil
}

// @Title CheckBuildInUsedSystem
// @Description  if buildId is relate to a  fleet ,you must delete fleet first.
// @Author wangnannan 2022-05-07 09:20:53 ${time}
// @Param buildId
// @Return e
func (s *Service) CheckBuildInUsedSystem(buildId string, projectId string) (e *errors.CodedError) {

	res, err := dao.CheckBuildInUsedSystem(buildId, projectId)
	if err != nil {
		return s.ErrorMsg(errors.ServerInternalError,
			"Check build relate fleet Failed!Search DB failed!", err)
	}
	if res == false {
		return s.ErrorMsg(errors.BuildIsInUseNotSupportDelete,
			"This build is in used! Can not Delete!", err)
	}
	return nil
}

// @Title DeleteByBuild
// @Description  Delete by buildId
// @Author wangnannan 2022-05-07 09:21:06 ${time}
// @Param buildId
// @Param projectId
// @Return e
func (s *Service) DeleteByBuild(buildId string, projectId string) (e *errors.CodedError) {

	err := dao.DeleteByBuild(buildId, projectId)
	if err != nil {
		return s.ErrorMsg(errors.ServerInternalError, "Build Delete error!", err)
	}

	s.Logger.Info("BuildId %s delete from db success!", buildId)

	return nil
}

func (s *Service) DeleteBuildInIMS(buildId string, projectId string) (e *errors.CodedError) {

	s.Logger.Info("BuildId %s start to delete build in IMS!", buildId)

	// get build info failed delete directly
	tmpBuild, err := dao.GetBuildById(buildId, projectId)
	if err != nil {
		s.Logger.Error("BuildId %s Get Build info failed %v!", buildId, err)
		return s.ErrorMsg(errors.ServerInternalError, "Get Build info failed!", err)
	}

	if tmpBuild.ImageId == "" {
		s.Logger.Warn("This build %s has no image in IMS", buildId)
		return nil
	}
	u, err := dao.GetUserResConfByOriProjectId(projectId)
	if err != nil {
		s.Logger.Error("get user res conf by project id %s failed %v", projectId, err)
		return s.ErrorMsg(errors.ServerInternalError, "get user res conf failed!", err)
	}

	imsClient := client.GetIMSClient(u.AccessKey, u.SecretAccessKey, u.Region, u.ProjectId)
	err = client.DeleteBuildInIMS(imsClient, tmpBuild.ImageId)
	if err != nil {
		return s.ErrorMsg(errors.ServerInternalError, "Delete build in IMS failed!", err)
	}

	return nil
}

// @Title DeleteBuildInObs
// @Description  Delete build and  bucket in obs
// @Author wangnannan 2022-05-07 09:21:19 ${time}
// @Param buildId
// @Param projectId
// @Return e
func (s *Service) DeleteBuildInObs(buildId string, projectId string) (e *errors.CodedError) {

	s.Logger.Info("BuildId %s start to delete build in OBS !", buildId)

	tmpBuild, err := dao.GetBuildById(buildId, projectId)
	if err != nil {
		s.Logger.Warn("BuildId %s Get Build info failed %v!", buildId, err)
		return s.ErrorMsg(errors.ServerInternalError, "Get Build info failed!", err)
	}

	u, err := dao.GetUserResConfByOriProjectId(projectId)
	if err != nil {
		s.Logger.Warn("get user res conf by project id %s failed %v", projectId, err)
		return s.ErrorMsg(errors.ServerInternalError, "get user res conf failed!", err)
	}

	obsClient, obserror := client.GetObsClient(u.AccessKey, u.SecretAccessKey, u.Region)
	if obserror != nil {
		return s.ErrorMsg(errors.ServerInternalError, "Delete build Get obsclient error!",
			obserror)
	}

	exist, obsErr := client.HeadBucket(obsClient, tmpBuild.StorageBucketName)
	if exist {
		s.Logger.Info("Bucket already exist.BucketName = %s ", tmpBuild.StorageBucketName)
		if obsErr == nil {
			err = client.DeleteObsObject(obsClient, tmpBuild.StorageKey, tmpBuild.StorageBucketName)
			if err != nil {
				return s.ErrorMsg(errors.ServerInternalError, "Delete build from OBS failed!", err)
			}

			// try to delete bucket TODO: bucket need to delete when user is quit
			client.DeleteObsBucket(obsClient, tmpBuild.StorageBucketName)

		} else {
			return s.ErrorMsg(errors.ServerInternalError, "Delete buildBucket from OBS failed!", obsErr)
		}
	} else {
		// if Bucket Not Exist return success
		if obserror != nil {
			return s.ErrorMsg(errors.ServerInternalError,
				"Delete buildBucket when HeadBucket from OBS failed!", obserror)
		}
	}

	obsClient.Close()
	s.Logger.Info("BuildId %s  object and bucket delete from obs success!", buildId)
	return nil

}

func (s *Service) DeleteBuildInSwr(buildId string, projectId string) (e *errors.CodedError) {
	s.Logger.Info("BuildId %s start to delete build in SWR !", buildId)

	// get build info failed delete directly
	tmpBuild, err := dao.GetBuildById(buildId, projectId)
	if err != nil {
		s.Logger.Error("BuildId %s Get Build info failed %v!", buildId, err)
		return s.ErrorMsg(errors.ServerInternalError, "Get Build info failed!", err)
	}

	u, err := dao.GetUserResConfByOriProjectId(projectId)
	if err != nil {
		s.Logger.Error("get user res conf by project id %s failed %v", projectId, err)
		return s.ErrorMsg(errors.ServerInternalError, "get user res conf failed!", err)
	}

	swrClient := client.GetSwrClient(u.AccessKey, u.SecretAccessKey, u.Region, u.ProjectId)
	imageId := tmpBuild.ImageId
	urlList := strings.Split(imageId, "/")
	// 数据库中的记录不正确 记录日志
	if len(urlList) <= 2 {
		s.Logger.Error("current image id is invalid %s", imageId)
		return nil
	}
	// 考虑到有变更名称/版本的情况,取url中的原始名称/版本
	nameList := strings.Split(urlList[2], ":")
	if len(nameList) <= 1 {
		s.Logger.Error("current image name and tag is invalid %s", urlList[2])
	}
	err = client.DeleteBuildSwrImages(swrClient, urlList[1], nameList[0], nameList[1])
	if err != nil {
		s.Logger.Error("delete images in swr failed failed %v", err)
		return s.ErrorMsg(errors.ServerInternalError, "delete images in swr failed!", err)
	}

	s.Logger.Info("BuildId %s  object and bucket delete from swr success!", buildId)
	return nil

}