// Package sqleditor add by blacknc <blacknc@163.com> at 2023/01/16 16:40:01
package sqleditor

import (
	"dataapps/commons/logger"
	dto "dataapps/internal/dto/sqleditor"
	entity "dataapps/internal/entity/sqleditor"
	service "dataapps/internal/service/sqleditor"
	"net/http"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	tiErrs "github.com/tidbcloud/tiinf/errs"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
)

// Create oplog godoc
// @Summary create oplog
// @Description create oplog
// @Tags sqleditor
// @Param org_id   path string true "org ID"
// @Param project_id path string true "project ID"
// @Param cluster_id path string true "cluster ID"
// @Param request_body body entity.SqlEditorOplogParam true "request_body"
// @Accept json
// @Produce json
// @Success 200 {object} dto.BasicRes
// @Router /orgs/{org_id}/projects/{project_id}/clusters/{cluster_id}/oplog [post]
func CreateOplog(c *gin.Context) {
	ctx := c.Request.Context()
	createdBy, hasErr := getUserEmail(c, ctx)
	if hasErr {
		c.Error(tiErrs.NewUnauthorized("User Not Found", nil))
		return
	}
	orgId, projectId, clusterId, err := getParam(c)
	if err != nil || orgId == 0 || projectId == 0 || clusterId == 0 {
		c.Error(tiErrs.NewBadRequest("Invalid Url Params", err))
		tilog.Error(ctx, "ERROR: get orgId,projectId,clusterId failed!", zap.Error(err), zap.String("user", createdBy))
		return
	}

	param := entity.SqlEditorOplogParam{}
	if err := bindJsonParam(c, &param); err != nil {
		tilog.Error(ctx, "CreateOplog bing param failed", zap.Error(err), zap.String("createdBy", createdBy))
		c.Error(err)
		return
	} else if !entity.ValidEventType[param.EventType] {
		tilog.Error(ctx, "invalid event_type", zap.String("createdBy", createdBy), zap.Any("param", param))
		c.Error(tiErrs.NewBadRequest("Invalid Log Event Type", nil))
		return
	}
	tilog.Info(ctx, "CreateOplog param", zap.String("createdBy", createdBy), logger.Redact("param", param))
	// Only accept sql_accept event
	if param.EventType != entity.EventTypeSqlAccept && param.EventType != entity.EventTypeGenSql {
		resJson := dto.DefaultSuccessBasicRes()
		c.JSON(http.StatusOK, resJson)
		return
	}

	startTime := time.UnixMilli(param.StartTime)
	endTime := time.UnixMilli(param.EndTime)
	duration := param.EndTime - param.StartTime
	if param.EventType == entity.EventTypeExecSql && param.Chat2QuerySId != "" {
		gensqlLog, err := service.ShowOplog(ctx, param.Chat2QuerySId, string(entity.EventTypeGenSql))
		if err != nil {
			c.Error(tiErrs.NewInternalDB("Get Oplog Failed", err))
			return
		} else if gensqlLog != nil {
			param.IsModified = !isContainSpl(gensqlLog.SqlContent, param.SqlContent)
			if param.UserQuestion == "" {
				param.UserQuestion = gensqlLog.UserQuestion
			}
		}
		param.IsAccept = true
	}
	if param.EventType == entity.EventTypeSqlAccept {
		param.Status = 1
	}

	if err := service.CreateOplog(ctx, entity.SqlEditorOplog{
		OrgId:         orgId,
		ProjectId:     projectId,
		ClusterId:     clusterId,
		SessionId:     param.SessionId,
		Chat2QuerySId: param.Chat2QuerySId,
		SqlFileId:     param.SqlFileId,
		EventType:     string(param.EventType),
		UserQuestion:  param.UserQuestion,
		SqlContent:    param.SqlContent,
		IsAccept:      param.IsAccept,
		IsModified:    param.IsModified,
		StartTime:     startTime.Format("2006-01-02 15:04:05"),
		EndTime:       endTime.Format("2006-01-02 15:04:05"),
		Duration:      duration,
		AffectedRows:  param.AffectedRows,
		Status:        param.Status,
		ErrorMessage:  param.ErrorMessage,
		CreatedBy:     createdBy,
		CreatedAt:     time.Now(),
		PromptVersion: param.PromptVersion,
	}); err != nil {
		tilog.Error(ctx, "CreateOplog failed", zap.Error(err))
		c.Error(tiErrs.NewInternalDB("Create Oplog Failed", err))
		return
	}

	resJson := dto.DefaultSuccessBasicRes()
	c.JSON(http.StatusOK, resJson)
}

func InnerCreateOplog(c *gin.Context) {
	ctx := c.Request.Context()
	var param entity.InnerSqlEditorOplogParam
	if err := bindJsonParam(c, &param); err != nil {
		tilog.Error(ctx, "InnerCreateOplog bing param failed", zap.Error(err))
		c.Error(tiErrs.NewBadRequest("Bind Params Failed", err))
		return
	} else if !entity.ValidEventType[param.EventType] {
		tilog.Error(ctx, "invalid event_type", zap.Any("param", param))
		c.Error(tiErrs.NewBadRequest("Invalid Log Event Type", nil))
		return
	}
	tilog.Info(ctx, "InnerCreateOplog param", zap.Any("param", param))

	duration := param.Duration
	if err := service.CreateOplog(ctx, entity.SqlEditorOplog{
		OrgId:           param.OrgID,
		ProjectId:       param.ProjectID,
		ClusterId:       param.ClusterID,
		Chat2QuerySId:   param.Chat2QuerySId,
		EventType:       string(param.EventType),
		UserQuestion:    param.UserQuestion,
		SqlContent:      param.SqlContent,
		IsAccept:        param.IsAccept,
		StartTime:       param.StartTime,
		EndTime:         param.EndTime,
		Duration:        duration,
		AffectedRows:    param.AffectedRows,
		AffectedColumns: param.AffectedColumns,
		Status:          param.Status,
		ErrorMessage:    param.ErrorMessage,
		CreatedBy:       param.CreatedBy,
		CreatedAt:       time.Now(),
		PromptVersion:   param.PromptVersion,
		Databases:       param.Databases,
		Channel:         param.Channel,
	}); err != nil {
		tilog.Error(ctx, "InnerCreateOplog failed", zap.Error(err))
		c.Error(tiErrs.NewInternalDB("Create Oplog Failed", err))
		return
	}

	resJson := dto.DefaultSuccessBasicRes()
	c.JSON(http.StatusOK, resJson)
}

func isContainSpl(s1, s2 string) bool {
	s1 = strings.TrimSpace(s1)
	s2 = strings.TrimSpace(s2)
	s1 = strings.ReplaceAll(s1, " \n", "")
	s2 = strings.ReplaceAll(s2, " \n", "")
	s1 = strings.ReplaceAll(s1, "\n", "")
	s2 = strings.ReplaceAll(s2, "\n", "")
	return strings.Contains(s1, s2)
}
