package middleware

import (
	"bytes"
	"cmdb-backend/models"
	"cmdb-backend/pkg/logging"
	"encoding/json"
	"fmt"
	"github.com/gin-contrib/sessions"
	"github.com/gin-gonic/gin"
	"io/ioutil"
	"strings"
)

type OperationMethod struct {
	C        *gin.Context
	Table    string `json:"table"`
	Desc     string `json:"desc"`
	Ip       string `json:"ip"`
	UserId   string `json:"userId"`
	Operator string `json:"operator"`
	From     string `json:"from"`
}

type body struct {
	Id     interface{} `json:"id" bson:"id"`
	Name   string      `json:"name" bson:"name"`
	Owner  string      `json:"owner" bson:"owner"`
	Remark string      `json:"remark" bson:"remark"`

	// DNS
	RecordIds []int  `json:"record_ids" bson:"record_ids"`
	ZoneName  string `json:"zone_name" bson:"zone_name"`
	Type      string `json:"type" bson:"type"`
	Line      string `json:"line" bson:"line"`
	Target    string `json:"target" bson:"target"`
	Status    string `json:"status" bson:"status"`

	//域名
	Possessor string `json:"possessor" bson:"possessor"`

	// 服务器
	InstanceId string `json:"instance_id" bson:"instance_id"`
	PrivateIp  string `json:"private_ip" bson:"private_ip"`
	ServerIds  []int  `json:"server_ids" bson:"server_ids"`
}

func Operation(handlerTableName map[string]string) gin.HandlerFunc {
	return func(c *gin.Context) {
		if strings.HasPrefix(c.Request.URL.Path, "/api/account") {
			c.Next()
			return
		}
		if strings.HasPrefix(c.Request.URL.Path, "/check_health") {
			c.Next()
			return
		}
		if c.Request.Method == "" {
			c.Next()
		}
		// 1. 获取当前用户信息
		from := "web"
		userId := sessions.Default(c).Get("user_id").(string)
		email := sessions.Default(c).Get("email")
		operatorName := strings.Split(email.(string), "@")[0]

		//TODO:解决通过 API 访问时，无法获取到可靠用户信息问题
		if userId == "" {
			from = "api"
			userId = c.GetHeader("user_id")
			operatorName = c.GetHeader("operator_name")
		}

		// 2. 获取表名 这里会把前面 "加载的包名.HandlerName"
		handlerName := strings.Split(c.HandlerName(), ".")[1]
		if _, ok := handlerTableName[handlerName]; !ok {
			c.Next() // 如果映射中没有找到表关系，说明没有手动加入
		}
		tableDesc := strings.Split(handlerTableName[handlerName], ",")
		if len(tableDesc) != 2 {
			return // 存在映射，但是不完全则返回
		}
		tableName := tableDesc[0] // 0表名
		desc := tableDesc[1]      // 1业务描述

		// 3. 获取Ip
		ip := c.ClientIP()

		// 4. 根据method方法名确定操作行为
		opMethod := &OperationMethod{
			C:        c,
			Ip:       ip,
			From:     from,
			UserId:   userId,
			Desc:     desc,
			Table:    tableName,
			Operator: operatorName,
		}
		switch c.Request.Method {
		case "PATCH":
			err := opMethod.patchOperation()
			if err != nil {
				c.Next()
			}
		case "POST":
			err := opMethod.postOperation()
			if err != nil {
				c.Next()
			}
		case "DELETE":
			err := opMethod.deleteOperation()
			if err != nil {
				c.Next()
			}
		default:
			return
		}
	}
}

func (o *OperationMethod) postOperation() (err error) {
	// 新建操作, 记录插入的数据 name
	var bodyBytes []byte
	if o.C.Request.Body != nil {
		bodyBytes, _ = ioutil.ReadAll(o.C.Request.Body)
	}
	o.C.Request.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))

	body := &body{}
	var createInfo interface{}
	_ = json.Unmarshal(bodyBytes, body)
	if body.Name != "" && o.Table == "dns_record" {
		o.C.Next()
		createInfo = fmt.Sprintf(
			"name: %v.%v, type: %v, line: %v, target: %v, owner: %v, remark: %v",
			body.Name, body.ZoneName, body.Type, body.Line, body.Target, body.Owner, body.Remark)
	} else if o.Table == "domain" {
		o.C.Next()
		createInfo = fmt.Sprintf(
			" name: %v, possessor: %v, owner: %v, remark: %v ",
			body.Name, body.Possessor, body.Owner, body.Remark)
	} else if o.Table == "server" {
		o.C.Next()
		createInfo = fmt.Sprintf(
			" server_ids: %v, name: %v, private_ip: %v, instance_id: %v, Owner: %v ",
			body.ServerIds, body.Name, body.PrivateIp, body.InstanceId, body.Owner)
	} else {
		createInfo = body.Name
	}

	operationLog := models.OperationLog{
		From:         o.From,
		OperatorId:   o.UserId,
		OperatorName: o.Operator,
		Operation:    "POST",
		Ip:           o.Ip,
		Business:     o.Desc,
		Tables:       o.Table,
		Data:         createInfo,
	}
	err = models.InsertLog(&operationLog)
	if err != nil {
		logging.Error("插入操作日志失败", err, map[string]interface{}{"info": createInfo, "table": o.Table})
		return err
	}
	return
}

func (o *OperationMethod) patchOperation() (err error) {
	// 获取body中的数据, 拿到操作的资源 id 列表
	var bodyBytes []byte
	if o.C.Request.Body != nil {
		bodyBytes, _ = ioutil.ReadAll(o.C.Request.Body)
	}
	o.C.Request.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes)) // 返回body中的原值

	body := &body{}
	var patchInfo interface{}
	_ = json.Unmarshal(bodyBytes, body)
	if o.Table == "dns_record" {
		o.C.Next()
		patchInfo = fmt.Sprintf(
			"id: %v, record_ids: %v, name: %v.%v, type: %v, line: %v, target: %v, status: %v, owner: %v, remark: %v",
			body.Id, body.RecordIds, body.Name, body.ZoneName, body.Type, body.Line, body.Target, body.Status, body.Owner, body.Remark)
	} else if o.Table == "domain" {
		o.C.Next()
		patchInfo = fmt.Sprintf(
			"id: %v, name: %v, possessor: %v, owner: %v, remark: %v ",
			body.Id, body.Name, body.Possessor, body.Owner, body.Remark)
	} else if o.Table == "server" {
		o.C.Next()
		patchInfo = fmt.Sprintf(
			"server_ids: %v, name: %v, private_ip: %v, instance_id: %v, Owner: %v ",
			body.ServerIds, body.Name, body.PrivateIp, body.InstanceId, body.Owner)
	} else {
		patchInfo = body.Id
	}

	operationLog := models.OperationLog{
		From:         o.From,
		OperatorId:   o.UserId,
		OperatorName: o.Operator,
		Operation:    "PATCH",
		Ip:           o.Ip,
		Business:     o.Desc,
		Tables:       o.Table,
		Data:         patchInfo,
	}
	err = models.InsertLog(&operationLog)
	if err != nil {
		logging.Error("插入操作日志失败", err, map[string]interface{}{"info": patchInfo, "table": o.Table})
		return err
	}
	return
}

func (o *OperationMethod) deleteOperation() (err error) {
	// 删除操作, 需要对删除的旧数据进行记录
	var bodyBytes []byte
	if o.C.Request.Body != nil {
		bodyBytes, _ = ioutil.ReadAll(o.C.Request.Body)
	}
	o.C.Request.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes)) // 返回body中的原值

	body := &body{}
	var deleteInfo interface{}
	_ = json.Unmarshal(bodyBytes, body)
	if body.Id != nil && o.Table == "dns_record" {
		o.C.Next()
		deleteInfo = fmt.Sprintf(
			"id: %v, name: %v.%v, type: %v, line: %v, target: %v, owner: %v, remark: %v",
			body.Id, body.Name, body.ZoneName, body.Type, body.Line, body.Target, body.Owner, body.Remark)
	} else if o.Table == "domain" {
		o.C.Next()
		deleteInfo = fmt.Sprintf(
			"id: %v, name: %v, possessor: %v, owner: %v, remark: %v ",
			body.Id, body.Name, body.Possessor, body.Owner, body.Remark)
	} else if o.Table == "server" {
		o.C.Next()
		deleteInfo = fmt.Sprintf(
			"server_ids: %v, name: %v, private_ip: %v, instance_id: %v, Owner: %v ",
			body.ServerIds, body.Name, body.PrivateIp, body.InstanceId, body.Owner)
	} else {
		deleteInfo = body.Id
	}

	operationLog := models.OperationLog{
		From:         o.From,
		OperatorId:   o.UserId,
		OperatorName: o.Operator,
		Operation:    "DELETE",
		Ip:           o.Ip,
		Business:     o.Desc,
		Tables:       o.Table,
		Data:         deleteInfo,
	}

	err = models.InsertLog(&operationLog)
	if err != nil {
		logging.Error("插入操作日志失败", err, map[string]interface{}{"info": deleteInfo, "table": o.Table})
		return err
	}
	return
}
