package storageManage

import (
	"encoding/json"
	"errors"
	"github.com/astaxie/beego"
	"k8s-console/dto"
	"k8s-console/dto/storageManage"
	"k8s-console/models"
	"k8s-console/utils"
	"k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes/scheme"
	"sigs.k8s.io/yaml"
	"strings"
)

var (
	infoLogger  = utils.InfoLogger
	errorLogger = utils.ErrorLogger
)

// PersistentVolumeController operations for PersistentVolume
type PersistentVolumeController struct {
	beego.Controller
}

// URLMapping ...
func (c *PersistentVolumeController) URLMapping() {
	c.Mapping("getPVList", c.GetPVList)
	c.Mapping("deletePV", c.DeletePV)
	c.Mapping("createPVByYaml", c.CreatePVByYaml)
}

// Post ...
// @Title GetPVList
// @Description get PVList
// @Param	lang		query 	string	true	"api result language type"
// @Param	body		body 	dto.GetPageListBaseRequestDto	true	""
// @Success 200 {object} dto.GetPageListBaseResponseDto
// @Failure 300 {object} dto.BaseResponseDto
// @router /getPVList [post]
func (c *PersistentVolumeController) GetPVList() {
	lang := c.GetString("lang")
	infoLogger.Println("语言: ", lang)
	var requestDto dto.GetPageListBaseRequestDto
	var errorResponseDto = dto.BaseResponseDto{300, utils.GetI18nLang(lang, "sysError")}
	var successResponseDto = dto.GetPageListBaseResponseDto{}
	successResponseDto.Status = 200
	if err := json.Unmarshal(c.Ctx.Input.RequestBody, &requestDto); err != nil {
		errorLogger.Println(err.Error())
		errorResponseDto.Message = utils.GetI18nLang(lang, "requestParamIncorrectFormat")
		c.Data["json"] = errorResponseDto
		c.ServeJSON()
		return
	}
	infoLogger.Println("查询所有存储卷请求参数: ", requestDto)

	pageIndex := requestDto.PageIndex
	pageSize := requestDto.PageSize
	if pageIndex < 0 {
		errorLogger.Println("页码必须大于等于0")
		errorResponseDto.Message = utils.GetI18nLang(lang, "pageIndexRange")
		c.Data["json"] = errorResponseDto
		c.ServeJSON()
		return
	}
	if pageSize <= 0 {
		errorLogger.Println("页大小必须大于0")
		errorResponseDto.Message = utils.GetI18nLang(lang, "pageSizeRange")
		c.Data["json"] = errorResponseDto
		c.ServeJSON()
		return
	}

	// 获取k8s客户端
	k8sClient := utils.GetK8sClient()
	if k8sClient == nil {
		errorResponseDto.Message = utils.GetI18nLang(lang, "createK8sConFail")
		c.Data["json"] = errorResponseDto
		c.ServeJSON()
		return
	}

	//查询集群所有pv信息
	pvList, err := k8sClient.CoreV1().PersistentVolumes().List(metav1.ListOptions{})
	if err != nil {
		errorLogger.Printf("查询集群所有存储卷失败: %s", err.Error())
		errorResponseDto.Message = utils.GetI18nLang(lang, "getPVList_getPVListFail")
		c.Data["json"] = errorResponseDto
		c.ServeJSON()
		return
	}
	itemList := pvList.Items
	totalRecords := len(itemList)
	infoLogger.Println("totalRecords:", totalRecords)
	totalPages := 0
	if totalRecords > 0 {
		if totalRecords <= pageIndex*pageSize {
			errorLogger.Println("页码超出最大页范围")
			errorResponseDto.Message = utils.GetI18nLang(lang, "pageIndexOutRange")
			c.Data["json"] = errorResponseDto
			c.ServeJSON()
			return
		}
		totalPages = totalRecords/pageSize + 1
		if totalRecords%pageSize == 0 {
			totalPages = totalRecords / pageSize
		}
	} else {
		if totalRecords < pageIndex*pageSize {
			errorLogger.Println("页码超出最大页范围")
			errorResponseDto.Message = utils.GetI18nLang(lang, "pageIndexOutRange")
			c.Data["json"] = errorResponseDto
			c.ServeJSON()
			return
		}
	}

	//默认截取到最后
	resultList := itemList[pageIndex*pageSize:]
	//最后部分大于页大小则截取最后部分的开头部分
	if totalRecords-pageIndex*pageSize >= pageSize {
		resultList = itemList[pageIndex*pageSize : (pageIndex+1)*pageSize]
	}
	result := []storageManage.GetPVListResultDataResponseDto{}
	for i, item := range resultList {
		infoLogger.Printf("-----------第%d个存储卷----------------", i+1)
		infoLogger.Println("name:", item.Name)
		storage := ""
		if v, ok := item.Spec.Capacity["storage"]; ok {
			storage = v.String()
		}
		infoLogger.Println("storage:", storage)
		accessModes := item.Spec.AccessModes
		infoLogger.Println("accessModes:", accessModes)
		accessModeList := []string{}
		for _, v := range accessModes {
			accessModeList = append(accessModeList, string(v))
		}
		persistentVolumeReclaimPolicy := item.Spec.PersistentVolumeReclaimPolicy
		infoLogger.Println("persistentVolumeReclaimPolicy:", persistentVolumeReclaimPolicy)
		//类型
		ty := ""
		persistentVolumeSource := item.Spec.PersistentVolumeSource
		infoLogger.Println("persistentVolumeSource:", persistentVolumeSource)
		var m map[string]interface{}
		b, _ := json.Marshal(persistentVolumeSource)
		json.Unmarshal(b, &m)
		infoLogger.Println("persistentVolumeSourceMap:", m)
		for k := range m {
			ty = k
		}
		infoLogger.Println("type:", ty)
		status := item.Status.Phase
		deletionTimestamp := item.DeletionTimestamp
		infoLogger.Println("DeletionTimestamp:", deletionTimestamp)
		if deletionTimestamp != nil {
			status = "Terminating"
		}
		infoLogger.Println("status:", status)
		creationTimestamp := utils.GetDurationToString(lang, item.CreationTimestamp.Time, true)
		infoLogger.Println("CreationTimestamp:", item.CreationTimestamp, " ", creationTimestamp)

		o := storageManage.GetPVListResultDataResponseDto{
			item.Name,
			storage,
			accessModeList,
			string(persistentVolumeReclaimPolicy),
			ty,
			string(status),
			creationTimestamp,
		}
		result = append(result, o)
	}

	successResponseDto.Result = result
	successResponseDto.PageInfo = dto.PageInfoBaseResponseDto{
		TotalRecords: totalRecords,
		TotalPages:   totalPages,
	}
	c.Data["json"] = successResponseDto
	c.ServeJSON()
}

// Post ...
// @Title DeletePV
// @Description delete PV
// @Param	lang		query 	string	true	"api result language type"
// @Param	body		body 	storageManage.DeletePVRequestDto	true	""
// @Success 200 {object} dto.BaseResponseDto
// @Failure 300 {object} dto.BaseResponseDto
// @router /deletePV [post]
func (c *PersistentVolumeController) DeletePV() {
	lang := c.GetString("lang")
	infoLogger.Println("语言: ", lang)
	body := c.Ctx.Input.RequestBody
	infoLogger.Println("删除存储卷请求参数:", string(body))
	var requestDto storageManage.DeletePVRequestDto
	var responseDto = dto.BaseResponseDto{300, utils.GetI18nLang(lang, "sysError")}
	if err := json.Unmarshal(body, &requestDto); err != nil {
		errorLogger.Println(err.Error())
		responseDto.Message = utils.GetI18nLang(lang, "requestParamIncorrectFormat")
		c.Data["json"] = responseDto
		c.ServeJSON()
		return
	}
	infoLogger.Println("删除存储卷请求参数: ", requestDto)

	pVName := requestDto.PVName
	if strings.TrimSpace(pVName) == "" {
		errorLogger.Println("存储卷名称不能为空")
		responseDto.Message = utils.GetI18nLang(lang, "requestParamIncorrectFormat")
		c.Data["json"] = responseDto
		c.ServeJSON()
		return
	}

	// 获取k8s客户端
	k8sClient := utils.GetK8sClient()
	if k8sClient == nil {
		responseDto.Message = utils.GetI18nLang(lang, "createK8sConFail")
		c.Data["json"] = responseDto
		c.ServeJSON()
		return
	}

	//查询pv信息
	pVInfo, err := k8sClient.CoreV1().PersistentVolumes().Get(pVName, metav1.GetOptions{})
	if err != nil {
		errStr := err.Error()
		errorLogger.Printf("查询存储卷:%s详情失败: %s", pVName, errStr)
		responseDto.Message = utils.GetI18nLang(lang, "deletePV_getPVInfoFail")
		if strings.Contains(errStr, "not found") {
			responseDto.Message = utils.GetI18nLang(lang, "deletePV_pVNotFound")
		}
		c.Data["json"] = responseDto
		c.ServeJSON()
		return
	}
	status := pVInfo.Status.Phase
	deletionTimestamp := pVInfo.DeletionTimestamp
	infoLogger.Println("DeletionTimestamp:", deletionTimestamp)
	if deletionTimestamp != nil {
		status = "Terminating"
	}
	infoLogger.Println("status:", status)
	if status == v1.VolumeBound {
		errorLogger.Println("不能删除状态为Bound的存储卷")
		responseDto.Message = utils.GetI18nLang(lang, "deletePV_cannotDeleteBoundStatusPV")
		c.Data["json"] = responseDto
		c.ServeJSON()
		return
	}

	infoLogger.Printf("执行删除名称为:%s的存储卷", pVName)
	deleteOpt := metav1.DeleteOptions{}
	//查询pv信息
	if err := k8sClient.CoreV1().PersistentVolumes().Delete(pVName, &deleteOpt); err != nil {
		errStr := err.Error()
		errorLogger.Printf("查询集群所有service失败: %s", errStr)
		responseDto.Message = utils.GetI18nLang(lang, "deletePV_deletePVFail")
		if strings.Contains(errStr, "not found") {
			responseDto.Message = utils.GetI18nLang(lang, "deletePV_pVNotFound")
		}
		c.Data["json"] = responseDto
		c.ServeJSON()
		return
	}
	infoLogger.Printf("删除名称为:%s的存储卷成功", pVName)

	responseDto.Status = 200
	responseDto.Message = utils.GetI18nLang(lang, "success")
	c.Data["json"] = responseDto
	c.ServeJSON()
}

// Post ...
// @Title CheckPVContent
// @Description check PVContent
// @Param	lang		query 	string	true	"api result language type"
// @Param	body		body 	storageManage.CheckPVContentRequestDto	true	""
// @Success 200 {object} dto.BaseResponseDto
// @Failure 300 {object} dto.BaseResponseDto
// @router /checkPVContent [post]
func (c *PersistentVolumeController) CheckPVContent() {
	lang := c.GetString("lang")
	infoLogger.Println("语言: ", lang)
	body := c.Ctx.Input.RequestBody
	infoLogger.Println("校验yaml文件创建存储卷请求参数: \n", string(body))
	var requestDto storageManage.CheckPVContentRequestDto
	var responseDto = dto.BaseResponseDto{300, utils.GetI18nLang(lang, "sysError")}
	if err := json.Unmarshal(body, &requestDto); err != nil {
		errorLogger.Println(err.Error())
		responseDto.Message = utils.GetI18nLang(lang, "requestParamIncorrectFormat")
		c.Data["json"] = responseDto
		c.ServeJSON()
		return
	}
	infoLogger.Println("校验通过yaml文件创建存储卷请求参数: ", requestDto)

	pVName := requestDto.ObjectMeta.Name
	if strings.TrimSpace(pVName) == "" {
		errorLogger.Println("存储卷名称不能为空")
		responseDto.Message = utils.GetI18nLang(lang, "requestParamIncorrectFormat")
		c.Data["json"] = responseDto
		c.ServeJSON()
		return
	}
	flag := false //是否包含主机信息
	annotations := requestDto.ObjectMeta.Annotations
	for k, v := range annotations {
		if k == "kubernetes.io/hostname" {
			if models.IsContainNode(v) {
				flag = true
			}
		}
	}
	if !flag {
		errorLogger.Println("存储卷annotations里面必须包含集群集群未禁止调度主机")
		responseDto.Message = utils.GetI18nLang(lang, "pvCheckErrorHostname")
		c.Data["json"] = responseDto
		c.ServeJSON()
		return
	}
	pVOptions := v1.PersistentVolume{}
	// 复制字段
	utils.StructCopy(&pVOptions, &requestDto)

	infoLogger.Println("执行校验创建存储卷请求参数")
	// 校验创建存储卷请求参数
	if err := checkPVParams(lang, pVOptions); err != nil {
		errorLogger.Printf("校验创建存储卷请求参数失败: %s", err.Error())
		responseDto.Message = err.Error()
		c.Data["json"] = responseDto
		c.ServeJSON()
		return
	}
	infoLogger.Println("执行校验创建存储卷请求参数成功")

	responseDto.Status = 200
	responseDto.Message = utils.GetI18nLang(lang, "success")
	c.Data["json"] = responseDto
	c.ServeJSON()
}

// Post ...
// @Title CreatePVByYaml
// @Description create PVByYaml
// @Param	lang		query 	string	true	"api result language type"
// @Param	body		body 	storageManage.CreatePVByYamlRequestDto	true	""
// @Success 200 {object} dto.BaseResponseDto
// @Failure 300 {object} dto.BaseResponseDto
// @router /createPVByYaml [post]
func (c *PersistentVolumeController) CreatePVByYaml() {
	lang := c.GetString("lang")
	infoLogger.Println("语言: ", lang)
	body := c.Ctx.Input.RequestBody
	infoLogger.Println("通过yaml文件创建存储卷请求参数: \n", string(body))
	var requestDto storageManage.CreatePVByYamlRequestDto
	var responseDto = dto.BaseResponseDto{300, utils.GetI18nLang(lang, "sysError")}
	if err := json.Unmarshal(body, &requestDto); err != nil {
		errorLogger.Println(err.Error())
		responseDto.Message = utils.GetI18nLang(lang, "requestParamIncorrectFormat")
		c.Data["json"] = responseDto
		c.ServeJSON()
		return
	}
	infoLogger.Println("通过yaml文件创建存储卷请求参数: ", requestDto)

	pVName := requestDto.ObjectMeta.Name
	if strings.TrimSpace(pVName) == "" {
		errorLogger.Println("存储卷名称不能为空")
		responseDto.Message = utils.GetI18nLang(lang, "requestParamIncorrectFormat")
		c.Data["json"] = responseDto
		c.ServeJSON()
		return
	}

	pVOptions := v1.PersistentVolume{}
	// 复制字段
	utils.StructCopy(&pVOptions, &requestDto)

	infoLogger.Println("执行校验创建存储卷请求参数")
	// 校验创建存储卷请求参数
	if err := checkPVParams(lang, pVOptions); err != nil {
		errorLogger.Printf("校验创建存储卷请求参数失败: %s", err.Error())
		responseDto.Message = err.Error()
		c.Data["json"] = responseDto
		c.ServeJSON()
		return
	}
	infoLogger.Println("执行校验创建存储卷请求参数成功")

	// 获取k8s客户端
	k8sClient := utils.GetK8sClient()
	if k8sClient == nil {
		responseDto.Message = utils.GetI18nLang(lang, "createK8sConFail")
		c.Data["json"] = responseDto
		c.ServeJSON()
		return
	}

	infoLogger.Printf("执行创建存储卷:%s", pVName)
	//创建存储卷
	if _, err := k8sClient.CoreV1().PersistentVolumes().Create(&pVOptions); err != nil {
		errStr := err.Error()
		errorLogger.Printf("执行创建存储卷:%s失败: %s", pVName, errStr)
		responseDto.Message = utils.GetI18nLang(lang, "createPVByYaml_createPVFail")
		c.Data["json"] = responseDto
		c.ServeJSON()
		return
	}
	infoLogger.Printf("执行创建存储卷:%s成功", pVName)

	responseDto.Status = 200
	responseDto.Message = utils.GetI18nLang(lang, "success")
	c.Data["json"] = responseDto
	c.ServeJSON()
}

// 校验创建存储卷请求参数
func checkPVParams(lang string, pVOptions v1.PersistentVolume) error {
	pVName := pVOptions.ObjectMeta.Name
	if strings.TrimSpace(pVName) == "" {
		errorLogger.Println("存储卷名称不能为空")
		return errors.New(utils.GetI18nLang(lang, "requestParamIncorrectFormat"))
	}

	// 转换yaml格式
	yamlBytes, err := yaml.Marshal(pVOptions)
	if err != nil {
		errorLogger.Printf("转换yaml失败: %s", err.Error())
		return errors.New(utils.GetI18nLang(lang, "checkPVParams_invalidPVFormat"))
	}
	infoLogger.Println("\n" + string(yamlBytes))

	decode := scheme.Codecs.UniversalDeserializer()
	if _, _, err = decode.Decode(yamlBytes, nil, nil); err != nil {
		errorLogger.Printf("转换yaml文件失败: %s", err.Error())
		return errors.New(utils.GetI18nLang(lang, "checkPVParams_invalidPVFormat"))
	}

	// 获取k8s客户端
	k8sClient := utils.GetK8sClient()
	if k8sClient == nil {
		return errors.New(utils.GetI18nLang(lang, "createK8sConFail"))
	}

	err = k8sClient.CoreV1().RESTClient().Post().
		SetHeader("Accept", "application/json; dry-run").
		SetHeader("Content-Type", "application/json; verify").
		Resource("persistentvolumes").
		Body(&pVOptions).
		Param("dryRun", "All").
		Do().Error()
	if err != nil {
		errStr := err.Error()
		errorLogger.Printf("校验运行失败: %s", errStr)
		errMsg := strings.Split(errStr, "\n")[0]
		if strings.Contains(errStr, "already exists") {
			errMsg = utils.GetI18nLang(lang, "createPVByYaml_pVAlreadyExists")
		}
		return errors.New(errMsg)
	}

	return nil
}
