package controller

import (
	"errors"
	"fmt"
	"net/http"
	"strconv"
	"strings"
	"time"

	"dataservices/commons/config"
	"dataservices/commons/ginparser"
	dto "dataservices/internal/dto/dataapi"
	"dataservices/internal/entity"
	service "dataservices/internal/service/central"

	dto2 "dataservices/internal/dto/dataapi_gateway"
	"github.com/tidbcloud/account/idl/pbgen/proto/account"
	"github.com/tidbcloud/dataservices/idl/pbgen/proto/dataapi"
	"github.com/tidbcloud/tiinf/errs"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"

	"github.com/gin-gonic/gin"
)

// Show Data App godoc
// @Summary show data app
// @Description show data app
// @Tags dataapi
// @Param org_id   path string true "org ID"
// @Param project_id path string true "project ID"
// @Param app_id path string true "app ID"
// @Accept json
// @Produce json
// @Success 200 {object} dto.GetDataAppRes
// @Router /orgs/{org_id}/projects/{project_id}/apps/{app_id} [get]
func GetDataApp(c *gin.Context) {
	ctx := c.Request.Context()
	validateKeys := &[]string{ginparser.PARAM_ORG_ID, ginparser.PARAM_PROJECT_ID, ginparser.PARAM_APP_ID}
	params := ginparser.NewCommonParamsByGin(c)
	createdBy, _ := params.GetUserEmail()
	validateErr := params.ValidateUrl(validateKeys)
	if validateErr != nil {
		tilog.Error(ctx, "DS-Central-/orgs/{org_id}/projects/{project_id}/apps/{app_id}[get]-GetDataApp param validate failed!", zap.Error(validateErr), zap.String("user", createdBy))
		c.Error(validateErr)
		return
	}
	resp, err := service.GetDataApp(ctx, params.AppID)
	if resp.AppVersion == "" {
		resp.AppVersion = "1.0.0"
	}
	if err != nil {
		tilog.Error(ctx, "DS-Central-/orgs/{org_id}/projects/{project_id}/apps/{app_id}[get]-GetDataApp execution failed!", zap.Error(err), zap.String("user", createdBy))
		c.Error(err)
		return
	}
	resJson := &dto.GetDataAppRes{
		BasicRes: dto.DefaultSuccessBasicRes(),
		Data:     resp,
	}
	c.JSON(http.StatusOK, resJson)
}

// Delete Data App godoc
// @Summary delete data app
// @Description delete data app
// @Tags dataapi
// @Param org_id   path string true "org ID"
// @Param project_id path string true "project ID"
// @Param app_id path string true "app ID"
// @Accept json
// @Produce json
// @Success 200 {object} dto.GenericSuccessApiResponse
// @Router /orgs/{org_id}/projects/{project_id}/apps/{app_id} [delete]
func DeleteDataApp(c *gin.Context) {
	ctx := c.Request.Context()
	validateKeys := &[]string{ginparser.PARAM_ORG_ID, ginparser.PARAM_PROJECT_ID, ginparser.PARAM_APP_ID}
	params := ginparser.NewCommonParamsByGin(c)
	createdBy, _ := params.GetUserEmail()
	validateErr := params.ValidateUrl(validateKeys)
	if validateErr != nil {
		tilog.Error(ctx, "DS-Central-/orgs/{org_id}/projects/{project_id}/apps/{app_id}[delete]-DeleteDataApp param validate failed!", zap.Error(validateErr), zap.String("user", createdBy))
		c.Error(validateErr)
		return
	}

	err := service.DeleteDataApp(ctx, params.AppID, createdBy, account.OperatorType_OPERATOR_TYPE_USER)
	if err != nil {
		tilog.Error(ctx, "DS-Central-/orgs/{org_id}/projects/{project_id}/apps/{app_id}[delete]-DeleteDataApp execution failed!", zap.Error(err), zap.String("user", createdBy))
		c.Error(err)
		return
	}

	c.JSON(http.StatusOK, dto.Success())
}

// Create Data App godoc
// @Summary create data app
// @Description create data app
// @Tags dataapi
// @Param org_id   path string true "org ID"
// @Param project_id path string true "project ID"
// @Param request_body body entity.CreateDataAppParam true "request_body"
// @Accept json
// @Produce json
// @Success 200 {object} dto.CreateDataAppRes
// @Router /orgs/{org_id}/projects/{project_id}/apps [post]
func CreateDataApp(c *gin.Context) {
	ctx := c.Request.Context()
	validateKeys := &[]string{ginparser.PARAM_ORG_ID, ginparser.PARAM_PROJECT_ID}
	params := ginparser.NewCommonParamsByGin(c)
	createdBy, _ := params.GetUserEmail()
	validateErr := params.ValidateUrl(validateKeys)
	if validateErr != nil {
		tilog.Error(ctx, "DS-Central-/orgs/{org_id}/projects/{project_id}/apps[post]-CreateDataApp param validate failed!", zap.Error(validateErr), zap.String("user", createdBy))
		c.Error(validateErr)
		return
	}

	var input entity.CreateDataAppParam
	err := params.BindParam(&input)
	if err != nil {
		tilog.Error(ctx, "DS-Central-/orgs/{org_id}/projects/{project_id}/apps[post]-CreateDataApp param bind failed!", zap.Error(err), zap.String("user", createdBy))
		c.Error(errs.NewBadRequest("Bind Params Failed", err))
		return
	}
	tilog.Info(ctx, "CreateDataApp payload", zap.Any("input", input), zap.String("user", createdBy))
	for _, clusterIDStr := range input.ClusterIDStrs {
		clusterID, err := strconv.ParseUint(clusterIDStr, 10, 64)
		if err != nil || clusterID == 0 {
			tilog.Error(ctx, "DS-Central-/orgs/{org_id}/projects/{project_id}/apps[post]-CreateDataApp Invalid params cluster_ids", zap.Error(err), zap.String("user", createdBy))
			c.Error(errs.NewBadRequest("Invalid params cluster_ids", err))
			return
		}
		input.ClusterIDs = append(input.ClusterIDs, clusterID)
	}
	if len(input.ClusterIDs) == 0 {
		c.Error(errs.NewBadRequest("The data app must be bound to at least one data source", nil))
		return
	}

	input.UserID, _ = params.GetUserId()
	dataAPP, endpoints, err := service.CreateDataApp(ctx, params.OrgID, params.ProjectID, createdBy, &input)
	if err != nil {
		tilog.Error(ctx, "DS-Central-/orgs/{org_id}/projects/{project_id}/apps[post]-CreateDataApp execution failed!", zap.Error(err), zap.String("user", createdBy))
		c.Error(err)
		return
	}

	var endpointSummary *dto.EndpointSummary
	if len(endpoints) > 0 {
		endpoint := endpoints[0]
		endpointSummary = &dto.EndpointSummary{
			ID:        endpoint.ID,
			Name:      endpoint.Name,
			DataappId: endpoint.DataappId,
			Type:      endpoint.Type,
			Method:    endpoint.Method,
			Endpoint:  endpoint.Endpoint,
			ClusterID: strconv.FormatUint(endpoint.ClusterId, 10),
			Vid:       endpoint.Vid,
		}
	}
	dtoAppInfo := service.ConvertDataappInfo(dataAPP)
	resJson := &dto.CreateDataAppRes{
		BasicRes: dto.DefaultSuccessBasicRes(),
		Data: &dto.CreateDataAppOut{
			AppInfo:  dtoAppInfo,
			Endpoint: endpointSummary,
		},
	}
	c.JSON(http.StatusOK, resJson)
}

// Update Data App godoc
// @Summary update data app
// @Description update data app
// @Tags dataapi
// @Param org_id   path string true "org ID"
// @Param project_id path string true "project ID"
// @Param app_id path string true "app ID"
// @Param request_body body entity.UpdateDataAppParam true "request_body"
// @Accept json
// @Produce json
// @Success 200 {object} dto.GenericSuccessApiResponse
// @Router /orgs/{org_id}/projects/{project_id}/apps/{app_id} [put]
func UpdateDataApp(c *gin.Context) {
	ctx := c.Request.Context()
	validateKeys := &[]string{ginparser.PARAM_ORG_ID, ginparser.PARAM_PROJECT_ID}
	params := ginparser.NewCommonParamsByGin(c)
	createdBy, _ := params.GetUserEmail()
	validateErr := params.ValidateUrl(validateKeys)
	if validateErr != nil {
		tilog.Error(ctx, "DS-Central-/orgs/{org_id}/projects/{project_id}/apps/{app_id}[put]-UpdateDataApp param validate failed!", zap.Error(validateErr), zap.String("user", createdBy))
		c.Error(validateErr)
		return
	}

	input := entity.UpdateDataAppParam{}
	err := params.BindParam(&input)
	if err != nil {
		tilog.Error(ctx, "DS-Central-/orgs/{org_id}/projects/{project_id}/apps/{app_id}[put]-UpdateDataApp param bind failed!", zap.Error(err), zap.String("user", createdBy))
		c.Error(err)
		return
	}
	tilog.Info(ctx, "UpdateDataApp payload", zap.Any("input", input), zap.String("user", createdBy))

	err = service.UpdateDataApp(ctx, params.AppID, &input, createdBy)
	if err != nil {
		tilog.Error(ctx, "DS-Central-/orgs/{org_id}/projects/{project_id}/apps/{app_id}[put]-UpdateDataApp execution failed!", zap.Error(err), zap.String("user", createdBy))
		c.Error(err)
		return
	}

	c.JSON(http.StatusOK, dto.Success())

}

// List Data App godoc
// @Summary list data app
// @Description list data app
// @Tags dataapi
// @Param org_id   path string true "org ID"
// @Param project_id path string true "project ID"
// @Param apptype query string true "app type, multiple split by commas. Example: dataapi,chat2query"
// @Accept json
// @Produce json
// @Success 200 {object} dto.ListDataAppRes
// @Router /orgs/{org_id}/projects/{project_id}/apps [get]
func ListDataApps(c *gin.Context) {
	ctx := c.Request.Context()
	validateKeys := &[]string{ginparser.PARAM_ORG_ID, ginparser.PARAM_PROJECT_ID}
	params := ginparser.NewCommonParamsByGin(c)
	createdBy, _ := params.GetUserEmail()
	validateErr := params.ValidateUrl(validateKeys)
	if validateErr != nil {
		tilog.Error(ctx, "DS-Central-/orgs/{org_id}/projects/{project_id}/apps[get]-ListDataApps param validate failed!", zap.Error(validateErr), zap.String("user", createdBy))
		c.Error(validateErr)
		return
	}
	appTypeStr := strings.TrimSpace(c.Query("apptype"))
	var appTypeList []string
	if appTypeStr != "" {
		appTypeList = strings.Split(appTypeStr, ",")
	}
	tilog.Info(ctx, "ListDataApps payload", zap.String("apptype", appTypeStr), zap.String("user", createdBy))
	resp, err := service.ListDataApps(ctx, params.ProjectID, appTypeList...)
	if err != nil {
		tilog.Error(ctx, "DS-Central-/orgs/{org_id}/projects/{project_id}/apps[get]-ListDataApps execution failed!", zap.Error(err), zap.String("user", createdBy))
		c.Error(err)
		return
	}

	appEndpointMap := make(map[uint64][]dto.EndpointSummary, len(resp))
	if len(resp) > 0 {
		appIDs := make([]uint64, 0, len(resp))
		for _, app := range resp {
			appIDs = append(appIDs, app.ID)
		}
		endpointList, err := service.ListEndpointsByAppIDs(ctx, appIDs)
		if err != nil {
			tilog.Error(ctx, "DS-Central-/orgs/{org_id}/projects/{project_id}/apps[get]-ListDataApps Get app's endpoint failed", zap.Error(err), zap.Uint64s("app_ids", appIDs), zap.String("user", createdBy))
			c.Error(err)
			return
		}
		endpointIDs := make([]uint64, 0, len(endpointList))
		for _, endpoint := range endpointList {
			endpointIDs = append(endpointIDs, endpoint.ID)
		}
		publishedEndpointMap, err := service.ListPublishedEndpointByIDs(ctx, endpointIDs)
		if err != nil {
			tilog.Error(ctx, "DS-Central-/orgs/{org_id}/projects/{project_id}/apps[get]-ListDataApps ListPublishedEndpointByIDs failed", zap.Error(err), zap.Uint64s("app_ids", appIDs), zap.String("user", createdBy))
			c.Error(err)
			return
		}
		for _, endpoint := range endpointList {
			isOnline := 0
			if endpoint.Label > 0 {
				isOnline = 1
			}
			var hasModified bool
			if publishedEndpoint := publishedEndpointMap[endpoint.ID]; publishedEndpoint != nil {
				hasModified = !endpoint.EqualPublished(publishedEndpoint)
			}
			appEndpointMap[endpoint.Endpoint.DataappId] = append(appEndpointMap[endpoint.Endpoint.DataappId], dto.EndpointSummary{
				ID:          endpoint.Endpoint.ID,
				DataappId:   endpoint.Endpoint.DataappId,
				Name:        endpoint.Endpoint.Name,
				Type:        endpoint.Endpoint.Type,
				Endpoint:    endpoint.Endpoint.Endpoint,
				Method:      endpoint.Endpoint.Method,
				ClusterID:   strconv.FormatUint(endpoint.Endpoint.ClusterId, 10),
				IsOnline:    isOnline,
				HasModified: hasModified,
				Vid:         endpoint.Vid,
			})
		}
	}

	dataAppList := make([]*dto.DataAppInfo, 0, len(resp))
	for _, app := range resp {
		dtoAppInfo := service.ConvertDataappInfo(app)
		dtoAppInfo.Endpoints = appEndpointMap[app.ID]
		var claims dto.DataappJWTClaims
		claims.AppID = app.ExternalAppID
		claims.Creater = createdBy
		claims.Sence = "openapi"
		token, err := claims.GenerateJWT()
		if err != nil {
			tilog.Error(ctx, "DS-Central-/orgs/{org_id}/projects/{project_id}/apps/{app_id}/exportlink[get]-GetDataappExportUrl GenerateJWT failed!", zap.Error(err), zap.String("user", createdBy))
		} else {
			dtoAppInfo.OpenApiLink = fmt.Sprintf("https://%s/api/v1/dataservices/external/appexport/openapi?token=%s", service.GetWebConsoleHost(), token)
		}
		dtoAppInfo.ApikeyMaxRPM = service.GetOrgApikeyRateLimit(app.OrgId)
		dataAppList = append(dataAppList, dtoAppInfo)
	}

	resJson := &dto.ListDataAppRes{
		BasicRes: dto.DefaultSuccessBasicRes(),
		Data:     dataAppList,
	}
	c.JSON(http.StatusOK, resJson)
}

// List Data App  Usage godoc
// @Summary list data app Usage
// @Description list data app Usage
// @Tags dataapi
// @Param org_id   path string true "org ID"
// @Param project_id path string true "project ID"
// @Param type path string true "Type"
// @Param month path int true "month"
// @Param year path int true "year"
// @Param class path string true "class"
// @Param region path string true "region"
// @Param dataapp_id path uint64  true "dataapp_id"
// @Param start path int64 true "start"
// @Param end path int64 true "end"
// @Param step path int true "step"
// @Accept json
// @Produce json
// @Success 200 {object} dto.OKType
// @Router /orgs/{org_id}/projects/{project_id}/list_dataapp_usage [get]
func ListDataAppUsage(c *gin.Context) {
	ctx := c.Request.Context()
	validateKeys := &[]string{ginparser.PARAM_ORG_ID, ginparser.PARAM_PROJECT_ID}
	params := ginparser.NewCommonParamsByGin(c)
	createdBy, _ := params.GetUserEmail()
	validateErr := params.ValidateUrl(validateKeys)
	if validateErr != nil {
		tilog.Error(ctx, "DS-Central-/orgs/{org_id}/projects/{project_id}/list_dataapp_usage[get]-ListDataAppUsage param validate failed!", zap.Error(validateErr), zap.String("user", createdBy))
		c.Error(validateErr)
		return
	}
	input := &entity.DataUsageQueryParam{}
	err := c.ShouldBindQuery(input)
	if err != nil {
		//tilog.Info(ctx, "ListDataAppUsage end ", zap.Error(err))
		tilog.Error(ctx, "DS-Central-/orgs/{org_id}/projects/{project_id}/list_dataapp_usage[get]-ListDataAppUsage bind param failed", zap.Error(err), zap.String("user", createdBy))
		c.Error(err)
		return
	}
	if len(input.Region) == 0 {
		c.JSON(http.StatusOK, entity.O11YResp{})

		return

	}
	resp, err := service.ListDataAppUsage(ctx, *input, params.ProjectID, params.OrgID)
	if err == nil {
		c.JSON(http.StatusOK, resp)

	} else {
		tilog.Error(ctx, "DS-Central-/orgs/{org_id}/projects/{project_id}/list_dataapp_usage[get]-ListDataAppUsage execution failed", zap.Error(err), zap.String("user", createdBy))
		c.JSON(http.StatusConflict, err)

	}
	tilog.Info(ctx, "ListDataAppUsage http  : ", zap.Any("resp", resp), zap.Error(err))

}

func CreateDDBTable(c *gin.Context) {
	ctx := c.Request.Context()
	params := struct {
		Region   string   `json:"region"`
		Replica  []string `json:"replica"`
		Tolerant bool     `json:"tolerant"`
	}{}
	if err := c.ShouldBind(&params); err != nil {
		c.Error(errs.NewBadRequest("Bind params failed", err))
		return
	}
	start := time.Now()
	if params.Region == "" {
		params.Region = config.Global().AwsConf.Region
	}
	err := service.CreateDynamoDBGlobalTable(ctx, params.Region, params.Replica, params.Tolerant)
	if err != nil {
		tilog.Error(ctx, "CreateDynamoDBGlobalTable failed", zap.Error(err))
		c.Error(err)
		return
	}
	tilog.Info(ctx, "RefreshDataapiAPP succeed", zap.String("cost", time.Since(start).String()))
	c.JSON(http.StatusOK, nil)
}

func SyncDataToDDB(c *gin.Context) {
	ctx := c.Request.Context()
	var params dto.SyncDataToDDBReq
	if err := c.ShouldBind(&params); err != nil {
		c.Error(errs.NewBadRequest("Bind params failed", err))
		return
	}
	start := time.Now()
	res, err := service.SyncDataToDDB(ctx, params)
	if err != nil {
		tilog.Error(ctx, "SyncDataToDDB failed", zap.Error(err))
		c.Error(err)
		return
	}
	tilog.Info(ctx, "SyncDataToDDB succeed", zap.String("cost", time.Since(start).String()))
	c.JSON(http.StatusOK, res)
}

func RefreshChat2QueryApp(c *gin.Context) {
	ctx := c.Request.Context()

	start := time.Now()
	err := service.RefreshChat2QueryApp(ctx)
	if err != nil {
		tilog.Error(ctx, "RefreshChat2QueryApp failed", zap.Error(err))
		c.AbortWithError(http.StatusInternalServerError, err)
		return
	}
	tilog.Info(ctx, "RefreshChat2Query succeed", zap.String("cost", time.Since(start).String()))
	c.JSON(http.StatusOK, nil)
}

func RefreshDataAPP(c *gin.Context) {
	ctx := c.Request.Context()
	appIDStr := c.Query("app_ids")
	var appIDs []uint64
	if appIDStr != "" {
		for _, appID := range strings.Split(appIDStr, ",") {
			if appIDInt, err := strconv.ParseUint(appID, 10, 64); err != nil {
				err = errs.NewBadRequest("failed to parse app_ids:"+err.Error(), err)
				tilog.Error(ctx, "invalid app_ids", zap.Error(err))
				c.Error(err)
				return
			} else {
				appIDs = append(appIDs, appIDInt)
			}
		}
	}
	start := time.Now()
	err := service.RefreshDataapiAPP(ctx, appIDs)
	if err != nil {
		tilog.Error(ctx, "RefreshDataapiAPP failed", zap.Error(err))
		c.Error(err)
		return
	}
	tilog.Info(ctx, "RefreshDataapiAPP succeed", zap.String("cost", time.Since(start).String()))
	c.JSON(http.StatusOK, nil)
}

// Export dataapp
// @Summary Export dataapp to postman collection
// @Description Export dataapp to postman collection
// @Tags dataapi
// @Param app_id path string true "app ID"
// @Accept json
// @Produce json
// @Success 200 {object} dto.DataappPostmanCollection
// @Router /external/appexport/postman [get]
func ExportDataappToPostmanCollection(c *gin.Context) {
	ctx := c.Request.Context()

	dataappClaims := c.MustGet(ginparser.CONTEXT_DATAAPP_JWT_CLAIMS).(*dto.DataappJWTClaims)
	if dataappClaims.Sence != "postman" {
		err := errs.NewBadRequest("The token is not supported in the current scene", nil)
		c.Error(err)
		return
	}
	resp, err := service.ExportDataappToPostmanCollection(ctx, dataappClaims.AppID)
	if err != nil {
		tilog.Error(ctx, "DS-Central-/external/appexport/postman[get]-ExportDataappToPostmanCollection execution failed!", zap.Error(err), zap.String("appid", dataappClaims.AppID))
		c.Error(err)
		return
	}
	c.JSON(http.StatusOK, resp)
}

// Get dataapp export url
// @Summary Get dataapp export url
// @Description Get dataapp export url
// @Tags dataapi
// @Param org_id   path string true "org ID"
// @Param project_id path string true "project ID"
// @Param app_id path string true "app ID"
// @Accept json
// @Produce json
// @Success 200 {object} dto.DataappExportLinkRes
// @Router /orgs/{org_id}/projects/{project_id}/apps/{app_id}/exportlink [get]
func GetDataappExportUrl(c *gin.Context) {
	ctx := c.Request.Context()
	validateKeys := &[]string{ginparser.PARAM_ORG_ID, ginparser.PARAM_PROJECT_ID, ginparser.PARAM_APP_ID}
	params := ginparser.NewCommonParamsByGin(c)
	createdBy, _ := params.GetUserEmail()
	validateErr := params.ValidateUrl(validateKeys)
	if validateErr != nil {
		tilog.Error(ctx, "DS-Central-/orgs/{org_id}/projects/{project_id}/apps/{app_id}/exportlink[get]-GetDataappExportUrl param validate failed!", zap.Error(validateErr), zap.String("user", createdBy))
		c.Error(validateErr)
		return
	}

	dataapp, err := service.GetDataApp(ctx, params.AppID)
	if err != nil {
		tilog.Error(ctx, "DS-Central-/orgs/{org_id}/projects/{project_id}/apps/{app_id}/exportlink[get]-GetDataApp failed!", zap.Error(err), zap.String("user", createdBy))
		c.Error(err)
		return
	}

	var claims dto.DataappJWTClaims
	claims.AppID = dataapp.ExternalAppID
	claims.Creater = createdBy
	claims.Sence = "postman"
	token, err := claims.GenerateJWT(dto.WithJWTExpire(12 * time.Hour))
	if err != nil {
		tilog.Error(ctx, "DS-Central-/orgs/{org_id}/projects/{project_id}/apps/{app_id}/exportlink[get]-GetDataappExportUrl GenerateJWT failed!", zap.Error(err), zap.String("user", createdBy))
		c.Error(err)
		return
	}
	link := fmt.Sprintf("https://%s/api/v1/dataservices/external/appexport/postman?token=%s", service.GetWebConsoleHost(), token)
	resJson := &dto.DataappExportLinkRes{
		BasicRes: dto.DefaultSuccessBasicRes(),
		Data:     link,
	}
	c.JSON(http.StatusOK, resJson)
}

// check run in postman status
// @Summary run in postman status
// @Description check run in postman status
// @Tags dataapi
// @Param org_id   path string true "org ID"
// @Param project_id path string true "project ID"
// @Param app_id path string true "app ID"
// @Accept json
// @Produce json
// @Success 200 {object} dto.CheckPostmanStatusRes
// @Router /orgs/{org_id}/projects/{project_id}/apps/{app_id}/postmanstatus [get]
func CheckPostmanStatus(c *gin.Context) {
	ctx := c.Request.Context()
	validateKeys := &[]string{ginparser.PARAM_ORG_ID, ginparser.PARAM_PROJECT_ID, ginparser.PARAM_APP_ID}
	params := ginparser.NewCommonParamsByGin(c)
	createdBy, _ := params.GetUserEmail()
	validateErr := params.ValidateUrl(validateKeys)
	if validateErr != nil {
		tilog.Error(ctx, "DS-Central-/orgs/{org_id}/projects/{project_id}/apps/{app_id}/postmanstatus[get]-CheckPostmanStatus param validate failed!", zap.Error(validateErr), zap.String("user", createdBy))
		c.Error(validateErr)
		return
	}

	status, err := service.DataappHasEndpointCanTest(ctx, params.AppID)
	if err != nil {
		tilog.Error(ctx, "DS-Central-/orgs/{org_id}/projects/{project_id}/apps/{app_id}/postmanstatus[get]-CheckPostmanStatus DataappHasEndpointCanRun failed!", zap.Error(err), zap.String("user", createdBy))
		c.Error(err)
		return
	}

	resJson := &dto.CheckPostmanStatusRes{
		BasicRes: dto.DefaultSuccessBasicRes(),
		Data:     status,
	}
	c.JSON(http.StatusOK, resJson)
}

// check sample dataapp status
// @Summary check sample dataapp status
// @Description check sample dataapp status
// @Tags dataapi
// @Param org_id   path string true "org ID"
// @Param project_id path string true "project ID"
// @Param app_id path string true "app ID"
// @Accept json
// @Produce json
// @Success 200 {object} dto.CheckSampleStatusRes
// @Router /orgs/{org_id}/projects/{project_id}/apps/{app_id}/samplestatus [get]
func CheckSampleStatus(c *gin.Context) {
	ctx := c.Request.Context()
	validateKeys := &[]string{ginparser.PARAM_ORG_ID, ginparser.PARAM_PROJECT_ID, ginparser.PARAM_APP_ID}
	params := ginparser.NewCommonParamsByGin(c)
	createdBy, _ := params.GetUserEmail()
	validateErr := params.ValidateUrl(validateKeys)
	if validateErr != nil {
		tilog.Error(ctx, "DS-Central-/orgs/{org_id}/projects/{project_id}/apps/{app_id}/samplestatus[get]-CheckSampleStatus param validate failed!", zap.Error(validateErr), zap.String("user", createdBy))
		c.Error(validateErr)
		return
	}

	appStatus, taskRecords, err := service.GetSampleDataappStates(ctx, params.AppID)
	if err != nil {
		tilog.Error(ctx, "DS-Central-/orgs/{org_id}/projects/{project_id}/apps/{app_id}/samplestatus[get]-CheckSampleStatus GetSampleDataappStates failed!", zap.Error(err), zap.String("user", createdBy))
		c.Error(err)
		return
	}

	var resJson dto.CheckSampleStatusRes
	resJson.BasicRes = dto.DefaultSuccessBasicRes()
	resJson.Data.AppStatus = appStatus
	resJson.Data.TaskRecords = taskRecords
	c.JSON(http.StatusOK, resJson)
}

// batch get sample dataapp code example
// @Summary batch get sample dataapp code example
// @Description batch get sample dataapp code example
// @Tags dataapi
// @Param org_id   path string true "org ID"
// @Param project_id path string true "project ID"
// @Param app_id path string true "app ID"
// @Accept json
// @Produce json
// @Success 200 {object} dto.BatchGetCodeExampleRes
// @Router /orgs/{org_id}/projects/{project_id}/apps/{app_id}/batchexample [get]
func BatchGetCodeExample(c *gin.Context) {
	ctx := c.Request.Context()
	validateKeys := &[]string{ginparser.PARAM_ORG_ID, ginparser.PARAM_PROJECT_ID, ginparser.PARAM_APP_ID}
	params := ginparser.NewCommonParamsByGin(c)
	createdBy, _ := params.GetUserEmail()
	validateErr := params.ValidateUrl(validateKeys)
	if validateErr != nil {
		tilog.Error(ctx, "DS-Central-/orgs/{org_id}/projects/{project_id}/apps/{app_id}/batchexample[get]-BatchGetCodeExample param validate failed!", zap.Error(validateErr), zap.String("user", createdBy))
		c.Error(validateErr)
		return
	}

	result, err := service.BatchGetCodeExample(ctx, params.AppID, createdBy)
	if err != nil {
		tilog.Error(ctx, "DS-Central-/orgs/{org_id}/projects/{project_id}/apps/{app_id}/batchexample[get]-BatchGetCodeExample BatchGetCodeExample failed!", zap.Error(err), zap.String("user", createdBy))
		c.Error(err)
		return
	}

	var resJson dto.BatchGetCodeExampleRes
	resJson.BasicRes = dto.DefaultSuccessBasicRes()
	resJson.Data.EndpointExamples = result
	c.JSON(http.StatusOK, resJson)
}

// update sample dataapp status
// @Summary update sample dataapp status
// @Description update sample dataapp status to normal
// @Tags dataapi
// @Param org_id   path string true "org ID"
// @Param project_id path string true "project ID"
// @Param app_id path string true "app ID"
// @Accept json
// @Produce json
// @Success 200 {object} dto.GenericSuccessApiResponse
// @Router /orgs/{org_id}/projects/{project_id}/apps/{app_id}/updatestatus [post]
func UpdateSampleStatusNormal(c *gin.Context) {
	ctx := c.Request.Context()
	validateKeys := &[]string{ginparser.PARAM_ORG_ID, ginparser.PARAM_PROJECT_ID, ginparser.PARAM_APP_ID}
	params := ginparser.NewCommonParamsByGin(c)
	createdBy, _ := params.GetUserEmail()
	validateErr := params.ValidateUrl(validateKeys)
	if validateErr != nil {
		tilog.Error(ctx, "DS-Central-/orgs/{org_id}/projects/{project_id}/apps/{app_id}/updatestatus[post]-UpdateSampleStatusNormal param validate failed!", zap.Error(validateErr), zap.String("user", createdBy))
		c.Error(validateErr)
		return
	}

	err := service.UpdateSampleDataappStatus(ctx, params.AppID)
	if err != nil {
		tilog.Error(ctx, "DS-Central-/orgs/{org_id}/projects/{project_id}/apps/{app_id}/updatestatus[post]-UpdateSampleStatusNormal UpdateSampleDataappStatus failed!", zap.Error(err), zap.String("user", createdBy))
		c.Error(err)
		return
	}

	c.JSON(http.StatusOK, dto.Success())
}

// Get system endpoint configuration
// @Summary Get system endpoint configuration
// @Tags dataapi
// @Param org_id   path string true "org ID"
// @Param project_id path string true "project ID"
// @Param app_id path string true "app ID"
// @Accept json
// @Produce json
// @Success 200 {object} dto.GetSystemEndpointConfigResp
// @Router /orgs/{org_id}/projects/{project_id}/apps/{app_id}/systemendpoint [get]
func GetSystemEndpointConfig(c *gin.Context) {
	ctx := c.Request.Context()
	validateKeys := &[]string{ginparser.PARAM_ORG_ID, ginparser.PARAM_PROJECT_ID, ginparser.PARAM_APP_ID}
	params := ginparser.NewCommonParamsByGin(c)
	createdBy, _ := params.GetUserEmail()
	validateErr := params.ValidateUrl(validateKeys)
	if validateErr != nil {
		tilog.Error(ctx, "DS-Central-/orgs/{org_id}/projects/{project_id}/apps/{app_id}/systemendpoint[get]-GetSystemEndpointConfig param validate failed!", zap.Error(validateErr), zap.String("user", createdBy))
		c.Error(validateErr)
		return
	}

	result, err := service.GetSystemEndpointConfig(ctx, params.AppID)
	if err != nil {
		tilog.Error(ctx, "DS-Central-/orgs/{org_id}/projects/{project_id}/apps/{app_id}/systemendpoint[get]-GetSystemEndpointConfig failed!", zap.Error(err), zap.String("user", createdBy))
		c.Error(err)
		return
	}

	var resJson dto.GetSystemEndpointConfigResp
	resJson.BasicRes = dto.DefaultSuccessBasicRes()
	resJson.Data = result
	c.JSON(http.StatusOK, resJson)
}

// Save system endpoint configuration
// @Summary Save system endpoint configuration
// @Tags dataapi
// @Param org_id   path string true "org ID"
// @Param project_id path string true "project ID"
// @Param app_id path string true "app ID"
// @Param request_body body dto.SystemEndpointConfigData true "request_body"
// @Accept json
// @Produce json
// @Success 200 {object} dto.GenericSuccessApiResponse
// @Router /orgs/{org_id}/projects/{project_id}/apps/{app_id}/systemendpoint [post]
func SaveSystemEndpointConfig(c *gin.Context) {
	ctx := c.Request.Context()
	validateKeys := &[]string{ginparser.PARAM_ORG_ID, ginparser.PARAM_PROJECT_ID, ginparser.PARAM_APP_ID}
	params := ginparser.NewCommonParamsByGin(c)
	createdBy, _ := params.GetUserEmail()
	validateErr := params.ValidateUrl(validateKeys)
	if validateErr != nil {
		tilog.Error(ctx, "DS-Central-/orgs/{org_id}/projects/{project_id}/apps/{app_id}/systemendpoint[post] param validate failed!", zap.String("user", createdBy), zap.Error(validateErr))
		c.Error(validateErr)
		return
	}

	// Get user auth0 token
	auth0Token, ok := getAuth0Token(c, ctx)
	if !ok {
		tilog.Error(ctx, "DS-Central-/orgs/{org_id}/projects/{project_id}/apps/{app_id}/systemendpoint[post] getAuth0Token failed!", zap.String("user", createdBy))
		c.Error(errs.ErrUnauthorized)
		return
	}

	var systemEndpointConfigData dto.SystemEndpointConfigData
	if err := params.BindParam(&systemEndpointConfigData); err != nil {
		tilog.Error(ctx, "DS-Central-/orgs/{org_id}/projects/{project_id}/apps/{app_id}/systemendpoint[post] param bind failed", zap.String("user", createdBy), zap.Error(err))
		c.Error(err)
		return
	}

	tilog.Info(ctx, "SaveSystemEndpointConfig begin", zap.Any("request", systemEndpointConfigData), zap.String("user", createdBy))
	credential := dto2.Credential{
		Type: dataapi.CrtType_AUTH0.String(),
		Key:  auth0Token,
	}
	err := service.SaveSystemEndpointConfig(ctx, params.ProjectID, params.AppID, &systemEndpointConfigData, createdBy, credential)
	if err != nil {
		tilog.Error(ctx, "DS-Central-/orgs/{org_id}/projects/{project_id}/apps/{app_id}/systemendpoint[post]-SaveSystemEndpointConfig failed!", zap.String("user", createdBy), zap.Error(err))
		c.Error(err)
		return
	}

	c.JSON(http.StatusOK, dto.Success())
}

// change log response body switch status
// @Summary change log response body switch status
// @Description change log response body switch status
// @Tags dataapi
// @Param org_id   path string true "org ID"
// @Param project_id path string true "project ID"
// @Param app_id path string true "app ID"
// @Param request_body body entity.ChangeLogResponseBodySwitchParam true "request_body"
// @Accept json
// @Produce json
// @Success 200 {object} dto.GenericSuccessApiResponse
// @Router /orgs/{org_id}/projects/{project_id}/apps/{app_id}/log_response_body_switch [post]
func ChangeLogResponseBodySwitch(c *gin.Context) {
	ctx := c.Request.Context()
	validateKeys := &[]string{ginparser.PARAM_ORG_ID, ginparser.PARAM_PROJECT_ID, ginparser.PARAM_APP_ID}
	params := ginparser.NewCommonParamsByGin(c)
	validateErr := params.ValidateUrl(validateKeys)
	createdBy, _ := params.GetUserEmail()
	if validateErr != nil {
		tilog.Error(ctx, "DS-Central-/orgs/{org_id}/projects/{project_id}/apps/{app_id}/log_response_body_switch[post]-GetSystemEndpointConfig param validate failed!", zap.Error(validateErr), zap.String("user", createdBy))
		c.Error(validateErr)
		return
	}
	var input entity.ChangeLogResponseBodySwitchParam
	err := params.BindParam(&input)
	if err != nil {
		tilog.Error(ctx, "DS-Central-/orgs/{org_id}/projects/{project_id}/log_response_body_switch[post]-CreateDataApp param bind failed!", zap.Error(err), zap.String("user", createdBy))
		c.Error(errs.NewBadRequest("Bind Params Failed", err))
		return
	}
	if input.NewStatus != entity.SwitchOff && input.NewStatus != entity.SwitchOn {
		c.Error(errs.NewBadRequest("invalid input", errors.New("invalid input")))
		return
	}
	err = service.UpdateDataAppLogResSwitch(ctx, params.AppID, input.NewStatus == entity.SwitchOn)
	if err != nil {
		tilog.Error(ctx, "DS-Central-/orgs/{org_id}/projects/{project_id}/log_response_body_switch[post]-SaveSystemEndpointConfig failed!", zap.String("user", createdBy), zap.Error(err))
		c.Error(err)
		return
	}
	res := dto.GenericSuccessApiResponse{}
	c.JSON(200, &res)
}
