package entity

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"sort"
	"strconv"
	"strings"

	"dataservices/commons/constants"
	"dataservices/commons/utils"

	jsoniter "github.com/json-iterator/go"
	"github.com/tidbcloud/tiinf/errs"
)

type (
	DataAppType                string
	DataAppDeploymentLogStatus string
	DataAppDeploymentLogOrigin string
	SnapshotDiffType           string
	GenSnapshotType            string
	ButtonType                 string
	ActionType                 string
)

// tablename
const (
	TableDataApps                  string = "dataapps"
	TableDataApiKeys               string = "dataapi_keys"
	TableEndpoints                 string = "endpoints"
	TableEndpointExtAttributes     string = "endpoint_ext_attributes"
	TableEndpointPublish           string = "endpoint_publish"
	TableEndpointPublishVersions   string = "endpoint_publish_versions"
	TableDataAPIConsoleMessage     string = "dataapi_console_message"
	TableDataAppClusters           string = "dataapp_clusters"
	TableDataAppDeploymentLogs     string = "dataapp_deployment_logs"
	TableDataAppDeploymentSettings string = "dataapp_deployment_settings"
	TableDataAppLogDownloadTask    string = "dataapp_log_download_task"
	TableDataAppAccessIPRecord     string = "datapp_access_ip_record"
	TableUserClusterInfo                  = "user_cluster_info"
	TableEndpointExecRecord               = "endpoint_exec_record"
	TableCommonTasks                      = "common_tasks"
	TableUserEndpointPlayground           = "user_endpoint_playground"

	TableEtlEndpointRegion = "etl_endpoint_region"
	TableEtlCombinedData   = "etl_combined_data"
	TableEtlActiveUser     = "etl_active_users"

	FieldTypeString string = "string"
	FieldTypeNumber string = "number"
	FieldTypeBool   string = "boolean"

	EXPIRESEC = 2 * 14400
)

// enum
const (
	DataAppTypeDataapi    DataAppType = "dataapi"
	DataAppTypeTrigger    DataAppType = "trigger"
	DataAppTypeChat2Query DataAppType = "chat2query"
	DataAppTypeCommon     DataAppType = "common"

	EndpointTypeChat2Sql      = "chat2sql"
	EndpointTypeChat2Data     = "chat2data"
	EndpointTypeChat2Chart    = "chat2chart"
	EndpointTypeChat2Question = "chat2question"
	EndpointTypeChat2EDA      = "chat2eda"
	EndpointTypeSystemData    = "system-data"

	DataAPISourceDataAPIConsole    = "dataapi-console"
	DataServiceSourceAdminAPI      = "dataservice-adminapi"
	DataAPISourceChat2QueryConsole = "chat2query-console"
	DataAPISourceVercelConsole     = "vercel-console"
	DataAPISourceExecEndpoint      = "exec-endpoint"

	ScopeTypeDATAAPP = "DATAAPP"

	AutoGenerateOperationGET         string = "GET"
	AutoGenerateOperationPUT         string = "PUT"
	AutoGenerateOperationBatchPUT    string = "B-PUT"
	AutoGenerateOperationPOST        string = "POST"
	AutoGenerateOperationBatchPOST   string = "B-POST"
	AutoGenerateOperationDELETE      string = "DELETE"
	AutoGenerateOperationBatchDELETE string = "B-DELETE"

	DataAppAdminAPICreateOrList = "dataapp-create-list"

	DataAppDeploymentStatusPending DataAppDeploymentLogStatus = "pending"
	DataAppDeploymentStatusRunning DataAppDeploymentLogStatus = "running"
	DataAppDeploymentStatusSuccess DataAppDeploymentLogStatus = "success"
	DataAppDeploymentStatusFailed  DataAppDeploymentLogStatus = "failed"

	DataAppDeploymentLogOriginUI     DataAppDeploymentLogOrigin = "UI"
	DataAppDeploymentLogOriginGithub DataAppDeploymentLogOrigin = "GitHub"

	SnapshotDiffTypeStruct SnapshotDiffType = "struct"
	SnapshotDiffTypeText   SnapshotDiffType = "text"

	GenSnapshotTypeDataAppDeploy    GenSnapshotType = "dataapp_deploy"
	GenSnapshotTypeEndpointDeploy   GenSnapshotType = "endpoint_deploy"
	GenSnapshotTypeEndpointUndeploy GenSnapshotType = "endpoint_undeploy"
	GenSnapshotTypeEndpointDelete   GenSnapshotType = "endpoint_delete"
	GenSnapshotTypeClusterCreate    GenSnapshotType = "cluster_create"
	GenSnapshotTypeClusterDelete    GenSnapshotType = "cluster_delete"
	GenSnapshotTypeDataAppUpdate    GenSnapshotType = "dataapp_update"

	ButtonHidden          ButtonType = "hidden"
	ButtonReviewAndDeploy ButtonType = "review_deploy"
	ButtonDeploy          ButtonType = "deploy"
	ButtonReviewDraft     ButtonType = "review_draft"

	ActionDoing   ActionType = "doing"
	ActionSucceed ActionType = "succeed"
	ActionFailed  ActionType = "failed"

	DataAppStatusInitializing = "initializing"
	DataAppStatusNormal       = "normal"

	// common task status
	CommonTaskStatusPending = "pending"
	CommonTaskStatusRunning = "running"
	CommonTaskStatusDormant = "dormant"
	CommonTaskStatusSucceed = "succeed"
	CommonTaskStatusFailed  = "failed"

	// common task type
	CommonTaskTypeSampleData  = "sample_data"
	CommonTaskTypePrivatelink = "private_link"

	SwitchOn  = "on"
	SwitchOff = "off"
)

// valid cache TTL
const (
	MinCacheTTL = 30
	MaxCacheTTL = 600
)

// Predefined app name
var PredefAppName = map[DataAppType]string{
	DataAppTypeDataapi:    "Data",
	DataAppTypeChat2Query: "Chat2Query",
	DataAppTypeTrigger:    "Trigger",
	DataAppTypeCommon:     "Common",
}

var ENDPOINTARGSNUMBER = []string{"tinyint", "smallint", "mediumint", "int", "bigint", "float", "double", "decimal"}

type CreateAPIKeyParam struct {
	Name         string `json:"name" binding:"required"`
	Role         string `json:"role" binding:"required"`
	RateLimitRpm int    `json:"rate_limit_rpm"`
	TTLInMin     uint32 `json:"ttl_in_min"`
}

type UpdateAPIKeyParam struct {
	Name         string `json:"name" binding:"required"`
	OldRole      string `json:"old_role"`
	NewRole      string `json:"new_role"`
	RateLimitRpm int    `json:"rate_limit_rpm"`
	OldTTLInMin  int    `json:"old_ttl_in_min"`
	NewTTLInMin  uint32 `json:"new_ttl_in_min"`
}

type DataAPIRawKey struct {
	DataAPIKey
	RawApiKey string `json:"raw_api_key"`
}

type DataAPIKey struct {
	ID            uint64         `gorm:"column:id" json:"id"`
	DataAppId     uint64         `gorm:"column:dataapp_id" json:"dataapp_id"`
	Source        string         `gorm:"column:source" json:"source"`
	EncryptApiKey string         `gorm:"column:api_key" json:"api_key"`
	CreatedBy     string         `gorm:"column:created_by" json:"created_by"`
	UpdatedBy     string         `gorm:"column:updated_by" json:"updated_by"`
	Description   string         `gorm:"column:description" json:"description"`
	IsDeleted     int8           `gorm:"column:is_deleted" json:"is_deleted"`
	CreatedAt     utils.JsonTime `gorm:"column:created_at" json:"created_at"`
	UpdatedAt     utils.JsonTime `gorm:"column:updated_at" json:"updated_at"`
}

type CreateDataAppParam struct {
	AppName          string   `json:"app_name" binding:"required"`
	DeploymentRegion string   `json:"deployment_region"`
	ClusterIDStrs    []string `json:"cluster_ids"`
	DeploymentConfig struct {
		PreflightLinkGithubParam
	} `json:"deployment_config"`
	Description string `json:"description"`
	AppVersion  string `json:"app_version"`
	IsSample    bool   `json:"is_sample"`

	AppType    DataAppType `json:"app_type" binding:"required,oneof=dataapi chat2query"`
	ClusterIDs []uint64    `json:"-"`
	UserID     uint64      `json:"-"`
}

type UpdateDataAppParam struct {
	AppName          string `json:"app_name" binding:"required"`
	DeploymentRegion string `json:"deployment_region"`
	Description      string `json:"description"`
	AppVersion       string `json:"app_version"`
}

type DataAppSettings struct {
	LogExecResult bool `dynamodbav:"log_exec_result" json:"log_exec_result"`
}

type DataApp struct {
	ID               uint64          `dynamodbav:"id" gorm:"column:id" json:"id"`
	OrgId            uint64          `dynamodbav:"org_id" gorm:"column:org_id" json:"org_id"`
	ProjectId        uint64          `dynamodbav:"project_id" gorm:"column:project_id" json:"project_id"`
	ExternalAppID    string          `dynamodbav:"external_app_id" gorm:"column:external_app_id" json:"external_app_id"`
	AppName          string          `dynamodbav:"-" gorm:"column:app_name" json:"app_name"`
	AppType          DataAppType     `dynamodbav:"app_type" gorm:"column:app_type" json:"app_type"`
	DeploymentRegion string          `dynamodbav:"-" gorm:"column:deployment_region" json:"deployment_region"`
	CreatedBy        string          `dynamodbav:"created_by" gorm:"column:created_by" json:"created_by"`
	UpdatedBy        string          `dynamodbav:"-" gorm:"column:updated_by" json:"updated_by"`
	IsDeleted        int8            `dynamodbav:"-" gorm:"column:is_deleted" json:"is_deleted"`
	CreatedAt        utils.JsonTime  `dynamodbav:"-" gorm:"column:created_at" json:"created_at"`
	UpdatedAt        utils.JsonTime  `dynamodbav:"-" gorm:"column:updated_at" json:"updated_at"`
	Description      string          `dynamodbav:"-" gorm:"column:description" json:"description"`
	AppVersion       string          `dynamodbav:"-" gorm:"column:app_version" json:"app_version"`
	OasAuthorized    bool            `dynamodbav:"-" gorm:"column:oas_authorized" json:"oas_authorized"`
	Status           string          `dynamodbav:"-" gorm:"column:status;<-:create" json:"status"`
	Settings         DataAppSettings `dynamodbav:"settings" gorm:"-" json:"settings"`
	SettingsJson     string          `dynamodbav:"-" gorm:"column:settings" json:"-"`
}

func (d *DataApp) FillSettings() error {
	if len(d.SettingsJson) == 0 {
		return nil
	}
	return jsoniter.UnmarshalFromString(d.SettingsJson, &d.Settings)
}

func (d1 *DataApp) Equal(d2 *DataApp) bool {
	if d1 == nil && d2 == nil {
		return true
	}
	if d1 == nil || d2 == nil {
		return false
	}
	return d1.OrgId == d2.OrgId &&
		d1.ProjectId == d2.ProjectId &&
		d1.ExternalAppID == d2.ExternalAppID &&
		d1.AppName == d2.AppName &&
		d1.AppType == d2.AppType &&
		d1.Description == d2.Description &&
		d1.AppVersion == d2.AppVersion
}

func (d1 *DataApp) GetStatus() string {
	if d1.Status == "" {
		return DataAppStatusNormal
	}
	return d1.Status
}

type Endpoint struct {
	ID          uint64         `dynamodbav:"id" gorm:"column:id" json:"id"`
	DataappId   uint64         `dynamodbav:"dataapp_id" gorm:"column:dataapp_id" json:"dataappp_id"`
	Name        string         `dynamodbav:"name" gorm:"column:name" json:"name"`
	Description string         `dynamodbav:"description" gorm:"column:description" json:"description"`
	Type        string         `dynamodbav:"type" gorm:"column:type" json:"type"`
	Endpoint    string         `dynamodbav:"endpoint" gorm:"column:endpoint" json:"endpoint"`
	Method      string         `dynamodbav:"method" gorm:"column:method" json:"method"`
	ClusterId   uint64         `dynamodbav:"cluster_id" gorm:"column:cluster_id" json:"cluster_id"`
	Database    string         `dynamodbav:"database" gorm:"column:database" json:"database"`
	SqlTemplate string         `dynamodbav:"sql_template" gorm:"column:sql_template" json:"sql_template"`
	Args        string         `dynamodbav:"args" gorm:"column:args" json:"args"`
	Settings    string         `dynamodbav:"settings" gorm:"column:settings" json:"settings"`
	CreatedBy   string         `dynamodbav:"created_by" gorm:"column:created_by" json:"created_by"`
	UpdatedBy   string         `dynamodbav:"updated_by" gorm:"column:updated_by" json:"updated_by"`
	CreatedAt   utils.JsonTime `dynamodbav:"-" gorm:"<-:created_at" json:"created_at"`
	UpdatedAt   utils.JsonTime `dynamodbav:"-" gorm:"column:updated_at" json:"updated_at"`
	RWMode      string         `dynamodbav:"rw_mode" gorm:"column:rw_mode" json:"rw_mode"`
	IsDeleted   int8           `dynamodbav:"-" gorm:"column:is_deleted" json:"is_deleted"`
	Tag         string         `dynamodbav:"tag" gorm:"column:tag" json:"tag"`
	Batch       bool           `dynamodbav:"batch" gorm:"column:batch" json:"batch"`
	Vid         uint64         `dynamodbav:"-" gorm:"column:vid" json:"vid"`
	Position    uint           `dynamodbav:"-" gorm:"column:position" json:"-"`

	PublishID uint64 `dynamodbav:"-" gorm:"-" json:"-"`
}

type EndpointV1 struct {
	Endpoint
	Label uint64 `json:"label"`
}
type AutoEndpointId struct {
	ID    uint64 `gorm:"column:id" json:"id"`
	AgeID uint64 `gorm:"column:age_id" json:"age_id"`
}
type EndpointPublish struct {
	ID          uint64         `dynamodbav:"-" gorm:"column:id" json:"id"`
	DataappId   uint64         `dynamodbav:"dataapp_id" gorm:"column:dataapp_id" json:"dataappp_id"`
	EndpointId  uint64         `dynamodbav:"endpoint_id" gorm:"column:endpoint_id" json:"endpoint_id"`
	Version     string         `dynamodbav:"version" gorm:"column:version" json:"version"`
	Type        string         `dynamodbav:"type" gorm:"column:type" json:"type"`
	Endpoint    string         `dynamodbav:"endpoint" gorm:"column:endpoint" json:"endpoint"`
	Method      string         `dynamodbav:"method" gorm:"column:method" json:"method"`
	ClusterId   uint64         `dynamodbav:"cluster_id" gorm:"column:cluster_id" json:"cluster_id"`
	Database    string         `dynamodbav:"database" gorm:"column:database" json:"database"`
	SqlTemplate string         `dynamodbav:"sql_template" gorm:"column:sql_template" json:"sql_template"`
	Args        string         `dynamodbav:"args" gorm:"column:args" json:"args"`
	Settings    string         `dynamodbav:"settings" gorm:"column:settings" json:"settings"`
	Tag         string         `dynamodbav:"tag" gorm:"column:tag" json:"tag"`
	Batch       bool           `dynamodbav:"batch" gorm:"column:batch" json:"batch"`
	CreatedBy   string         `dynamodbav:"created_by" gorm:"column:created_by" json:"created_by"`
	UpdatedBy   string         `dynamodbav:"updated_by" gorm:"column:updated_by" json:"updated_by"`
	CreatedAt   utils.JsonTime `dynamodbav:"-" gorm:"column:created_at" json:"created_at"`
	UpdatedAt   utils.JsonTime `dynamodbav:"-" gorm:"column:updated_at" json:"updated_at"`
	RWMode      string         `dynamodbav:"rw_mode" gorm:"column:rw_mode" json:"rw_mode"`

	Name        string `dynamodbav:"-" gorm:"-" json:"-"`
	Description string `dynamodbav:"-" gorm:"-" json:"-"`
}

type EndpointPublishVersions struct {
	ID          uint64         `gorm:"column:id" json:"id"`
	DataappId   uint64         `gorm:"column:dataapp_id" json:"dataappp_id"`
	EndpointId  uint64         `gorm:"column:endpoint_id" json:"endpoint_id"`
	Name        string         `gorm:"column:name" json:"name"`
	Description string         `gorm:"column:description" json:"description"`
	Type        string         `gorm:"column:type" json:"type"`
	Endpoint    string         `gorm:"column:endpoint" json:"endpoint"`
	Method      string         `gorm:"column:method" json:"method"`
	ClusterId   uint64         `gorm:"column:cluster_id" json:"cluster_id"`
	Database    string         `gorm:"column:database" json:"database"`
	SqlTemplate string         `gorm:"column:sql_template" json:"sql_template"`
	Args        string         `gorm:"column:args" json:"args"`
	Settings    string         `gorm:"column:settings" json:"settings"`
	Version     string         `gorm:"column:version" json:"version"`
	IsOnline    int8           `gorm:"column:is_online" json:"is_online"`
	CreatedBy   string         `gorm:"column:created_by" json:"created_by"`
	CreatedAt   utils.JsonTime `gorm:"column:created_at" json:"created_at"`
	UpdatedAt   utils.JsonTime `gorm:"column:updated_at" json:"updated_at"`
	RWMode      string         `gorm:"column:rw_mode" json:"rw_mode"`
}

type EndpointSettings struct {
	TimeOut          int  `json:"timeout"`
	RowsLimit        int  `json:"row_limit"`
	CacheEnabled     int8 `json:"cache_enabled"`
	CacheTTL         int  `json:"cache_ttl"`
	EnablePagination int  `json:"enable_pagination"` // enable pagination, 0 or 1
}

func (s *EndpointSettings) IsCacheEnabled() bool {
	return s.CacheEnabled == 1
}

func (s *EndpointSettings) TTLValid() bool {
	return s.CacheTTL >= MinCacheTTL && s.CacheTTL <= MaxCacheTTL
}

func (s1 EndpointSettings) Equal(s2 EndpointSettings) bool {
	return s1.TimeOut == s2.TimeOut &&
		s1.RowsLimit == s2.RowsLimit &&
		s1.CacheEnabled == s2.CacheEnabled &&
		s1.CacheTTL == s2.CacheTTL &&
		s1.EnablePagination == s2.EnablePagination
}

type CreateEndpointParam struct {
	Name         string           `json:"name"`
	Description  string           `json:"description"`
	Endpoint     string           `json:"endpoint"`
	EndpointID   uint64           `json:"-"`
	Method       string           `json:"method"`
	ClusterId    uint64           `json:"-"`
	ClusterIdStr string           `json:"cluster_id"`
	Database     string           `json:"database"`
	SqlTemplate  string           `json:"sql_template"`
	Settings     EndpointSettings `json:"settings"`
	Args         []EndpointArgs   `json:"args"`
	Tag          string           `json:"tag"`
	Batch        bool             `json:"batch"`
	Vid          uint64           `json:"vid,string"`
}

func (p *CreateEndpointParam) Parse() error {
	if p.ClusterIdStr == "" || p.ClusterIdStr == "0" {
		return nil
	}
	var err error
	p.ClusterId, err = strconv.ParseUint(p.ClusterIdStr, 10, 64)
	if err != nil {
		return errs.NewBadRequest("invalid cluster_id", err)
	}
	return err
}

func (p *CreateEndpointParam) EqualEndpoint(endpoint *Endpoint, selectedKey map[string]any) bool {

	var endpointSettings EndpointSettings
	if endpoint.Settings != "" {
		if err := utils.FromJson(endpoint.Settings, &endpointSettings); err != nil {
			return false
		}
	}

	if endpoint.IsSystemDataEndpoint() {
		return endpointSettings.Equal(p.Settings)
	}

	var endpointArgsList EndpointArgsList
	if endpoint.Args != "" {
		if err := utils.FromJson(endpoint.Args, &endpointArgsList); err != nil {
			return false
		}
	}

	if len(selectedKey) > 0 {
		params, err := utils.ConvertObjToMap(p)
		if err != nil {
			return false
		}
		endpointObj, err := utils.ConvertObjToMap(endpoint)
		if err != nil {
			return false
		}
		for k := range selectedKey {
			if _, ok := params[k]; !ok {
				continue
			}
			var res bool
			switch k {
			case "settings":
				res = endpointSettings.Equal(p.Settings)
			case "args":
				res = endpointArgsList.Equal(p.Args)
			case "vid":
				res = true
			case "cluster_id":
				res = p.ClusterId == endpoint.ClusterId
			default:
				res = params[k] == endpointObj[k]
			}
			if !res {
				return false
			}
		}
		return true
	}

	result := p.Name == endpoint.Name &&
		p.Description == endpoint.Description &&
		p.EndpointID == endpoint.ID &&
		p.Endpoint == endpoint.Endpoint &&
		p.Method == endpoint.Method &&
		endpointSettings.Equal(p.Settings) &&
		endpointArgsList.Equal(p.Args) &&
		p.Batch == endpoint.IsBatch() &&
		p.Tag == endpoint.Tag
	if !result {
		return false
	}
	if !endpoint.IsSystemDataEndpoint() {
		return p.ClusterId == endpoint.ClusterId &&
			p.Database == endpoint.Database &&
			p.SqlTemplate == endpoint.SqlTemplate
	}
	return true
}

const (
	EndpointArgTypeString  = "string"
	EndpointArgTypeNumber  = "number"
	EndpointArgTypeInteger = "integer"
	EndpointArgTypeArray   = "array"
	EndpointArgTypeBoolean = "boolean"
)

type DataRes struct {
	Columns []Column `json:"columns"`
	Rows    []Row    `json:"rows"`
	Result  Result   `json:"result"`
}

type BatchDataRes struct {
	Columns []Column       `json:"columns"`
	Rows    []BatchRowsStr `json:"rows"`
	Result  Result         `json:"result"`
}

type Result struct {
	Code         int64    `json:"code"`
	Message      string   `json:"message"`
	StartMs      int64    `json:"start_ms"`
	EndMs        int64    `json:"end_ms"`
	Latency      string   `json:"latency"`
	RowCount     int64    `json:"row_count"`
	RowAffect    int64    `json:"row_affect"`
	Limit        uint64   `json:"limit"`
	Sql          string   `json:"sql,omitempty"`
	AiLatency    string   `json:"ai_latency,omitempty"`
	Databases    []string `json:"databases,omitempty"`
	WarnCount    int      `json:"warn_count,omitempty"`
	WarnMessages []string `json:"warn_messages,omitempty"`
}

type Row map[string]string

type Column struct {
	Col      string `json:"col"`
	DataType string `json:"data_type"`
	Nullable bool   `json:"nullable"`
}

type DataAPIAdvancedMessage struct {
	ID            uint64         `json:"id"`
	DataappId     uint64         `json:"dataapp_id"`
	ExternalAppId string         `json:"external_app_id"`
	Type          string         `json:"type"`
	Path          string         `json:"path"`
	Method        string         `json:"database"`
	Sql           string         `json:"sql"`
	StartTime     string         `json:"start_time"`
	EndTime       string         `json:"end_time"`
	Status        string         `json:"status"`
	ErrorMessage  string         `json:"error_message"`
	Duration      string         `json:"duration"`
	CreatedBy     string         `json:"created_by"`
	CreatedAt     utils.JsonTime `gorm:"column:created_at" json:"created_at"`
	UpdatedAt     utils.JsonTime `gorm:"column:updated_at" json:"updated_at"`
}

type DataAPIAccess struct {
	ApiKey *DataAPIKey
	App    *DataApp
}

type EndpointParamsParam struct {
	SqlTemplate string `json:"sql_template"`
}

type CreateSessionParam struct {
	ClusterId uint64 `json:"cluster_id,string" binding:"required"`
	Database  string `json:"database"`
}

type StatementParam struct {
	SessionID string           `json:"session_id" binding:"required"`
	ClusterId uint64           `json:"cluster_id,string" binding:"required"`
	Database  string           `json:"database"`
	Sql       string           `json:"sql" binding:"required"`
	Args      []StatementArgs  `json:"args"`
	Settings  EndpointSettings `json:"settings"`

	OrgId      uint64 `json:"-"`
	ProjectId  uint64 `json:"-"`
	DataappId  uint64 `json:"-"`
	Auth0Token string `json:"-"`
	CreatedBy  string `json:"-"`
}

type StatementParamV2 struct {
	SessionID    string            `json:"session_id" binding:"required"`
	ClusterIdStr string            `json:"cluster_id" binding:"required"`
	Database     string            `json:"database"`
	Sql          string            `json:"sql" binding:"required"`
	Args         [][]StatementArgs `json:"args"`
	Settings     EndpointSettings  `json:"settings"`

	OrgId      uint64 `json:"-"`
	ProjectId  uint64 `json:"-"`
	ClusterId  uint64 `json:"-"`
	DataappId  uint64 `json:"-"`
	Auth0Token string `json:"-"`
	CreatedBy  string `json:"-"`
}

type ShowDBParam struct {
	ProjectId  uint64 `json:"-"`
	DataappId  uint64 `json:"-"`
	ClusterId  uint64 `json:"-"`
	Auth0Token string `json:"-"`
	ShowSysDB  string `json:"-"`
}

type ShowTableParam struct {
	ProjectId  uint64 `json:"-"`
	DataappId  uint64 `json:"-"`
	ClusterId  uint64 `json:"-"`
	DBName     string `json:"-"`
	Auth0Token string `json:"-"`
}
type ShowVectorFieldParam struct {
	ProjectId  uint64 `json:"-"`
	DataappId  uint64 `json:"-"`
	ClusterId  uint64 `json:"-"`
	DBName     string `json:"-"`
	TableName  string `json:"-"`
	Auth0Token string `json:"-"`
}

type StatementArgs struct {
	EndpointArgs
	Value any `json:"value"`
}

type TestEndpointArgs struct {
	Name  string `json:"name" binding:"required"`
	Type  string `json:"type" binding:"required"`
	Value any    `json:"value"`
}

type TestEndpointParam struct {
	Args []TestEndpointArgs `json:"args"`

	ProjectId  uint64 `json:"-"`
	AppId      uint64 `json:"-"`
	EndpointId uint64 `json:"-"`
	Auth0Token string `json:"-"`
	Operator   string `json:"-"`
}

type TestEndpointParamV2 struct {
	Args [][]TestEndpointArgs `json:"args"`

	ProjectId  uint64 `json:"-"`
	AppId      uint64 `json:"-"`
	EndpointId uint64 `json:"-"`
	CredType   string `json:"-"`
	CredKey    string `json:"-"`
	Operator   string `json:"-"`
}

type DataAPIConsoleMessage struct {
	ID           uint64         `json:"id"`
	OrgId        uint64         `json:"org_id"`
	ProjectId    uint64         `json:"project_id"`
	DataappId    uint64         `json:"dataapp_id"`
	ClusterId    uint64         `json:"cluster_id"`
	Sql          string         `json:"sql"`
	Args         string         `json:"args"`
	ConvertSql   string         `json:"convert_sql"`
	StartTime    utils.JsonTime `json:"start_time"`
	EndTime      utils.JsonTime `json:"end_time"`
	Status       string         `json:"status"`
	ErrorMessage string         `json:"error_message"`
	Duration     int64          `json:"duration"`
	CreatedBy    string         `json:"created_by"`
	CreatedAt    utils.JsonTime `json:"created_at"`
	UpdatedAt    utils.JsonTime `json:"updated_at"`
}

type CodeExampleInput struct {
	ClusterID      string
	Database       string
	AK             string
	SK             string
	AuthType       string
	Language       string
	FillDefaultVal bool
	Operator       string
}

type AppCluster struct {
	ID        uint64         `gorm:"column:id" json:"id" dynamodbav:"-"`
	DataappID uint64         `gorm:"column:dataapp_id" json:"dataapp_id" dynamodbav:"dataapp_id"`
	ClusterID uint64         `gorm:"column:cluster_id" json:"cluster_id" dynamodbav:"cluster_id"`
	CreatedBy string         `gorm:"column:created_by" json:"created_by" dynamodbav:"-"`
	UpdatedBy string         `gorm:"column:updated_by" json:"updated_by" dynamodbav:"-"`
	CreatedAt utils.JsonTime `gorm:"column:created_at" json:"created_at" dynamodbav:"created_at"`
	UpdatedAt utils.JsonTime `gorm:"column:updated_at" json:"updated_at" dynamodbav:"-"`
	IsDeleted int8           `gorm:"column:is_deleted" json:"is_deleted" dynamodbav:"-"`
}

type UserClusterInfo struct {
	OrgId           uint64 `dynamodbav:"org_id"`
	ProjectId       uint64 `dynamodbav:"project_id"`
	ClusterId       uint64 `dynamodbav:"cluster_id"`
	IsDedicate      bool   `dynamodbav:"is_dedicate"`
	Cloud           string `dynamodbav:"cloud"`
	Region          string `dynamodbav:"region"`
	EndpointService string `dynamodbav:"endpoint_service"`
	AZ              string `dynamodbav:"az"`
	EndpointId      string `dynamodbav:"endpoint_id"`
	PrivateDNS      string `dynamodbav:"private_dns"`
	Host            string `dynamodbav:"host"`
	Port            uint64 `dynamodbav:"port"`
}

type AutoGenerateEndpoint struct {
	ClusterId              string                     `json:"cluster_id"`
	Region                 string                     `json:"region"`
	DB                     string                     `json:"db"`
	Table                  string                     `json:"table"`
	Operations             []string                   `json:"operations"`
	VectorSearchOperations *VectorSearchOperations    `json:"vector_search_operations"`
	Configuration          *AutoGenerateConfiguration `json:"configuration"`
}

type VectorSearchOperations struct {
	IsEnabled    bool   `json:"is_enabled"`
	Field        string `json:"field"`
	DistanceFunc string `json:"distance_func"`
}

type AutoGenerateConfiguration struct {
	IsDeploy bool   `json:"is_deploy"`
	TimeOut  int    `json:"timeout"`
	Batch    bool   `json:"batch"`
	Tag      string `json:"tag"`
}

type VerifyBatch struct {
	Sql    string `json:"sql"`
	Method string `json:"method"`
}

//type BatchRows struct {
//	Index   int    `json:"index"`
//	Success bool   `json:"success"`
//	Message string `json:"message"`
//	ID      int64  `json:"id"`
//}

type BatchRowsStr struct {
	Index           string `json:"index"`
	Success         string `json:"success"`
	Message         string `json:"message"`
	AutoIncrementID string `json:"auto_increment_id"`
}

/*
CREATE TABLE `dataapp_deployment_logs` (

	`id` bigint(25) unsigned NOT NULL AUTO_INCREMENT,
	`dataapp_id` bigint(25) unsigned NOT NULL DEFAULT 0,
	`name` varchar(255) DEFAULT '',
	`origin` varchar(255) DEFAULT '' COMMENT 'UI / Github Webhook',
	`commit_id` varchar(255) DEFAULT NULL COMMENT 'github commit id',
	`diff_url` varchar(255) DEFAULT NULL COMMENT 'github diff_url',
	`created_by` varchar(255) NOT NULL DEFAULT '',
	`status` varchar(255) DEFAULT NULL COMMENT 'pending/running/success/fail',
	`status_error_message` text DEFAULT NULL,
	`snapshot` text DEFAULT NULL COMMENT 'snapshot content',
	`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
	`deployed_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
	PRIMARY KEY (`id`),
	KEY `idx_dataapp_id_created_at` (`dataapp_id`,`created_at`),
	KEY `idx_status` (`status`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin AUTO_INCREMENT=360001
*/
type DataAppDeploymentLog struct {
	ID                 uint64                     `gorm:"column:id" json:"id"`
	DataappID          uint64                     `gorm:"column:dataapp_id" json:"dataapp_id"`
	Name               string                     `gorm:"column:name" json:"name"`
	Origin             DataAppDeploymentLogOrigin `gorm:"column:origin" json:"origin"`
	CommitID           string                     `gorm:"column:commit_id" json:"commit_id"`
	DiffUrl            string                     `gorm:"column:diff_url" json:"diff_url"`
	CreatedBy          string                     `gorm:"column:created_by" json:"created_by"`
	Status             DataAppDeploymentLogStatus `gorm:"column:status" json:"status"`
	StatusErrorMessage string                     `gorm:"column:status_error_message" json:"status_error_msg"`
	Snapshot           string                     `gorm:"column:snapshot" json:"snapshot"`
	CreatedAt          utils.JsonTime             `gorm:"column:created_at" json:"created_at"`
	UpdatedAt          utils.JsonTime             `gorm:"column:updated_at" json:"updated_at"`
	DeployedAt         utils.JsonTime             `gorm:"column:deployed_at" json:"deployed_at"`
	RedeployFromID     uint64                     `gorm:"column:redeploy_from_id" json:"redeploy_from_id"`
	FinishedAt         utils.JsonTime             `gorm:"column:finished_at" json:"finished_at"`
	RanAt              utils.JsonTime             `gorm:"column:ran_at" json:"ran_at"`
}

type DeployDataAppParam struct {
	DeployName string `json:"deploy_name"`
}

type SaveDeploymentLogParam struct {
	DeployName string `json:"deploy_name" binding:"required"`
}

type DataAppSnapshot struct {
	DataApp   *DataApp      `json:"dataapp"`
	Endpoints []*Endpoint   `json:"endpoints"`
	Clusters  []*AppCluster `json:"clusters"`
}

type DataAppConfigJson struct {
	AppId       string `json:"app_id"`
	AppName     string `json:"app_name"`
	AppType     string `json:"app_type"`
	Description string `json:"description"`
	AppVersion  string `json:"app_version"`
}

type DataSource struct {
	ClusterId uint64 `json:"cluster_id"`
}

type HttpEndpointConfigJson struct {
	Name        string                        `json:"name"`
	Description string                        `json:"description"`
	Method      string                        `json:"method"`
	Endpoint    string                        `json:"endpoint"`
	DataSource  *DataSource                   `json:"data_source,omitempty"`
	Params      []*HttpEndpointConfigJsonArgs `json:"params"`
	Settings    *EndpointSettings             `json:"settings"`
	Tag         string                        `json:"tag"`
	Batch       int8                          `json:"batch_operation"`
	SqlFile     string                        `json:"sql_file"`
	Type        string                        `json:"type"`
	ReturnType  string                        `json:"return_type"`
}

type DataSourceClusterJson struct {
	ClusterId uint64 `json:"cluster_id"`
}

type HttpEndpointConfigJsonArgs struct {
	Name            string `json:"name"`
	Type            string `json:"type"` // string/bigint/bigint unsigned/double/array/enum/replace, if type is array/enum, should also specify the value-type.
	Required        int8   `json:"required"`
	Default         string `json:"default"`
	Description     string `json:"description"`
	Enum            string `json:"enum,omitempty"`
	ItemType        string `json:"item_type,omitempty"`
	IsPathParameter bool   `json:"is_path_parameter,omitempty"`
}

func (a *HttpEndpointConfigJsonArgs) Equal(b *HttpEndpointConfigJsonArgs) bool {
	if a == nil && b == nil {
		return true
	} else if a == nil || b == nil {
		return false
	}
	return a.Name == b.Name &&
		a.Type == b.Type &&
		a.Required == b.Required &&
		a.Default == b.Default &&
		a.Description == b.Description &&
		a.Enum == b.Enum &&
		a.ItemType == b.ItemType &&
		a.IsPathParameter == b.IsPathParameter
}

func (a *HttpEndpointConfigJsonArgs) DiffDetail(b *HttpEndpointConfigJsonArgs) map[string]interface{} {
	result := make(map[string]interface{})
	if a == nil && b == nil {
		return nil
	} else if a == nil || b == nil {
		result["a"] = a
		result["b"] = b
		return result
	}
	result["name"] = utils.CompareDiff(a.Name, b.Name)
	result["type"] = utils.CompareDiff(a.Type, b.Type)
	result["required"] = utils.CompareDiff(a.Required, b.Required)
	result["default"] = utils.CompareDiff(a.Default, b.Default)
	result["description"] = utils.CompareDiff(a.Description, b.Description)
	result["enum"] = utils.CompareDiff(a.Enum, b.Enum)
	result["item_type"] = utils.CompareDiff(a.ItemType, b.ItemType)
	return result
}

type HttpEndpointArgsList []*HttpEndpointConfigJsonArgs

func (a HttpEndpointArgsList) Equal(b HttpEndpointArgsList) bool {
	if len(a) != len(b) {
		return false
	}

	aMap := make(map[string]*HttpEndpointConfigJsonArgs, len(a))
	bMap := make(map[string]*HttpEndpointConfigJsonArgs, len(b))
	nameMap := make(map[string]bool, len(a))
	for i := range a {
		aMap[a[i].Name] = a[i]
		nameMap[a[i].Name] = true
	}
	for i := range b {
		bMap[b[i].Name] = b[i]
		nameMap[b[i].Name] = true
	}
	for name := range nameMap {
		if !aMap[name].Equal(bMap[name]) {
			return false
		}
	}
	return true
}
func (a HttpEndpointArgsList) DiffDetail(b HttpEndpointArgsList) []map[string]interface{} {
	aMap := make(map[string]*HttpEndpointConfigJsonArgs, len(a))
	bMap := make(map[string]*HttpEndpointConfigJsonArgs, len(b))
	nameMap := make(map[string]bool, len(a))
	for i := range a {
		aMap[a[i].Name] = a[i]
		nameMap[a[i].Name] = true
	}
	for i := range b {
		bMap[b[i].Name] = b[i]
		nameMap[b[i].Name] = true
	}

	var diff []map[string]interface{}
	for name := range nameMap {
		diff = append(diff, aMap[name].DiffDetail(bMap[name]))
	}
	return diff
}

type SqlFile struct {
	FileName string
	Content  []byte
}

func (s *DataAppSnapshot) DataAppConfigContent() ([]byte, error) {
	if s.DataApp == nil {
		return []byte("{}"), nil
	}
	jsonObj := DataAppConfigJson{
		AppId:       s.DataApp.ExternalAppID,
		AppName:     s.DataApp.AppName,
		AppType:     "dataapp",
		Description: s.DataApp.Description,
		AppVersion:  s.DataApp.AppVersion,
	}
	return json.MarshalIndent(jsonObj, "", "  ")
}
func (s *DataAppSnapshot) SqlFileName(endpoint *Endpoint) string {
	return fmt.Sprintf("%s%s.sql", endpoint.Method, strings.ReplaceAll(endpoint.Endpoint, "/", "-"))
}

func EndpointInnerTypeToGithubType(endpointType string) string {
	switch endpointType {
	case EndpointTypeChat2Sql:
		return constants.EndpointResTypeSQL
	case EndpointTypeSystemData:
		return EndpointTypeSystemData
	default:
		return constants.EndpointResTypeChat2Data
	}
}

func EndpointGithubTypeToInnerType(endpointType string) string {
	switch endpointType {
	case EndpointTypeSystemData:
		return EndpointTypeSystemData
	default:
		return EndpointTypeChat2Sql
	}
}

func (s *DataAppSnapshot) HttpEndpointConfigContent() ([]byte, error) {
	jsonObj := make([]HttpEndpointConfigJson, 0)
	for _, endpoint := range s.Endpoints {
		params := make([]*HttpEndpointConfigJsonArgs, 0)
		var settings EndpointSettings
		if endpoint.Args != "" {
			err := json.Unmarshal([]byte(endpoint.Args), &params)
			if err != nil {
				return nil, err
			}
		}
		if endpoint.Settings != "" {
			err := json.Unmarshal([]byte(endpoint.Settings), &settings)
			if err != nil {
				return nil, err
			}
		}
		description, err := url.PathUnescape(endpoint.Description)
		if err != nil {
			return nil, err
		}
		batch := 0
		if endpoint.IsBatch() {
			batch = 1
		}
		endpointConf := HttpEndpointConfigJson{
			Name:        endpoint.Name,
			Description: description,
			Method:      endpoint.Method,
			Endpoint:    endpoint.Endpoint,
			Params:      params,
			Settings:    &settings,
			Tag:         endpoint.Tag,
			Type:        EndpointInnerTypeToGithubType(endpoint.Type),
			ReturnType:  "json",
		}
		if !endpoint.IsSystemDataEndpoint() {
			endpointConf.DataSource = &DataSource{ClusterId: endpoint.ClusterId}
			endpointConf.SqlFile = fmt.Sprintf("sql/%s", s.SqlFileName(endpoint))
			endpointConf.Batch = int8(batch)
		}
		jsonObj = append(jsonObj, endpointConf)
	}
	return json.MarshalIndent(jsonObj, "", "  ")
}

func (s *DataAppSnapshot) SqlFiles() []*SqlFile {
	result := make([]*SqlFile, 0)
	for _, endpoint := range s.Endpoints {
		if endpoint.IsSystemDataEndpoint() {
			continue
		}
		result = append(result, &SqlFile{
			FileName: s.SqlFileName(endpoint),
			Content:  []byte(endpoint.SqlTemplate),
		})
	}
	return result
}

func (s *DataAppSnapshot) DataSourceClusterContent() ([]byte, error) {
	jsonObj := make([]DataSourceClusterJson, 0)
	for _, cluster := range s.Clusters {
		jsonObj = append(jsonObj, DataSourceClusterJson{ClusterId: cluster.ClusterID})
	}
	return json.MarshalIndent(jsonObj, "", "  ")
}

func (s *DataAppSnapshot) Normalize() {
	sort.Slice(s.Endpoints, func(i, j int) bool {
		si := fmt.Sprintf("%s:%s", s.Endpoints[i].Method, s.Endpoints[i].Endpoint)
		sj := fmt.Sprintf("%s:%s", s.Endpoints[j].Method, s.Endpoints[j].Endpoint)
		return si < sj
	})
	sort.Slice(s.Clusters, func(i, j int) bool {
		return s.Clusters[i].ClusterID < s.Clusters[j].ClusterID
	})
}

/*
CREATE TABLE `dataapp_deployment_settings` (

	`id` bigint(25) unsigned NOT NULL AUTO_INCREMENT,
	`dataapp_id` bigint(25) unsigned NOT NULL DEFAULT 0,
	`enable_ui_drafts` BOOLEAN default 0,
	`enable_automatic_deployment` BOOLEAN default 0,
	`automatic_deployment_provider` varchar(255) default "",
	`github_installation_id` bigint NOT NULL DEFAULT 0,
	`github_repository_id` bigint(25) unsigned NOT NULL DEFAULT 0,
	`github_branch` VARCHAR(255) DEFAULT NULL,
	`github_directory` VARCHAR(255) DEFAULT NULL,
	`has_deploy_conflict` BOOLEAN default 0,
	`automatic_deployment_setting`  text DEFAULT NULL,
	UNIQUE KEY `idx_dataapp_id` (`dataapp_id`),
	KEY `idx_github_repostory_id_branch_directory` (`github_repository_id`, `github_branch`, `github_directory`),
	PRIMARY KEY (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
*/
type DataAppDeploymentSetting struct {
	ID                   uint64 `gorm:"column:id" json:"id"`
	DataappID            uint64 `gorm:"column:dataapp_id" json:"dataapp_id"`
	EnableUIDrafts       bool   `gorm:"column:enable_ui_drafts" json:"enable_ui_drafts"`
	EnableAutoDeploy     bool   `gorm:"column:enable_automatic_deployment" json:"enable_automatic_deployment"`
	Provider             string `gorm:"column:automatic_deployment_provider" json:"automatic_deployment_provider"`
	GithubInstallationId int64  `gorm:"column:github_installation_id" json:"github_installation_id"`
	GithubRepoID         int64  `gorm:"column:github_repository_id" json:"github_repo_id"`
	GithubBranch         string `gorm:"column:github_branch" json:"github_branch"`
	GithubDirectory      string `gorm:"column:github_directory" json:"github_directory"`
	HasDeployConflict    bool   `gorm:"column:has_deploy_conflict" json:"has_deploy_conflict"`
	Settings             string `gorm:"column:automatic_deployment_setting" json:"automatic_deployment_setting"`
}

type LinkGithubParam struct {
	LinkGithubInnerParam
	EnableAutoDeploy bool `json:"enable_automatic_deployment"`
}

type LinkGithubInnerParam struct {
	InstallationId int64  `json:"installation_id"`
	RepoId         int64  `json:"repository_id"`
	Branch         string `json:"branch"`
	Directory      string `json:"directory"`
}

type PreflightLinkGithubParam struct {
	LinkGithubInnerParam
	PreflightRequestId string `json:"preflight_request_id"`
}

type DeploymentSwithConfig struct {
	EnableUIDrafts   bool `json:"enable_ui_drafts"`
	EnableAutoDeploy bool `json:"enable_automatic_deployment"`
}

type DataAppDeploymentConfig struct {
	PreflightLinkGithubParam
	DeploymentSwithConfig
}

type DataAppDiff struct {
	Modify *DataApp `json:"modify"`
}

func (f *DataAppDiff) HasDiff() bool {
	return f.Modify != nil
}

type EndpointsDiff struct {
	NoChange        []*Endpoint              `json:"no_change"`
	Added           []*Endpoint              `json:"added"`
	Modified        []*Endpoint              `json:"modified"`
	Deleted         []*Endpoint              `json:"deleted"`
	ModifiedPublish []*EndpointPublish       `json:"modified_publish"`
	DeletedPublish  []*EndpointPublish       `json:"deleted_publish"`
	ModifiedDetail  []map[string]interface{} `json:"modified_detail"`
}

func (f EndpointsDiff) HasDiff() bool {
	return len(f.Added) != 0 || len(f.Modified) != 0 || len(f.DeletedPublish) != 0
}

type SnapshotDiff struct {
	TextDiff        string          `json:"text_diff"`
	DataAppDiff     *DataAppDiff    `json:"data_app_diff"`
	EndpointsDiff   *EndpointsDiff  `json:"endpoints_diff"`
	AppClustersDiff *AppClusterDiff `json:"app_clusters_diff"`
	HasDiff         bool            `json:"has_diff"`

	DeploymentLog *DataAppDeploymentLog `json:"-"`
}

type AppClusterDiff struct {
	Added   []uint64 `json:"added"`
	Deleted []uint64 `json:"deleted"`
}

func (f AppClusterDiff) HasDiff() bool {
	return len(f.Added) != 0 || len(f.Deleted) != 0
}

func (publish *EndpointPublish) IsBatch() bool {
	if publish.Method == http.MethodDelete {
		return false
	}
	return publish.Batch
}

func (publish *EndpointPublish) ToEndpoint() *Endpoint {
	var res = &Endpoint{
		ID:          publish.EndpointId,
		DataappId:   publish.DataappId,
		Name:        publish.Name,
		Description: publish.Description,
		Type:        publish.Type,
		Endpoint:    publish.Endpoint,
		Method:      publish.Method,
		ClusterId:   publish.ClusterId,
		Database:    publish.Database,
		SqlTemplate: publish.SqlTemplate,
		Args:        publish.Args,
		Settings:    publish.Settings,
		RWMode:      publish.RWMode,
		CreatedBy:   publish.CreatedBy,
		UpdatedBy:   publish.UpdatedBy,
		PublishID:   publish.ID,
		Tag:         publish.Tag,
		Batch:       publish.IsBatch(),
	}
	return res
}

func (endpoint *Endpoint) ToPublished() *EndpointPublish {
	var res = &EndpointPublish{
		EndpointId:  endpoint.ID,
		DataappId:   endpoint.DataappId,
		Type:        endpoint.Type,
		Endpoint:    endpoint.Endpoint,
		Method:      endpoint.Method,
		ClusterId:   endpoint.ClusterId,
		Database:    endpoint.Database,
		SqlTemplate: endpoint.SqlTemplate,
		Args:        endpoint.Args,
		Settings:    endpoint.Settings,
		RWMode:      endpoint.RWMode,
		CreatedBy:   endpoint.CreatedBy,
		UpdatedBy:   endpoint.UpdatedBy,
		CreatedAt:   endpoint.CreatedAt,
		UpdatedAt:   endpoint.UpdatedAt,
		Tag:         endpoint.Tag,
		Batch:       endpoint.IsBatch(),
	}
	return res
}

func (endpoint *Endpoint) EqualPublished(publish *EndpointPublish) bool {
	var endpointArgs HttpEndpointArgsList
	var publishArgs HttpEndpointArgsList
	if endpoint.Args != "" {
		if err := json.Unmarshal([]byte(endpoint.Args), &endpointArgs); err != nil {
			return false
		}
	}
	if publish.Args != "" {
		if err := json.Unmarshal([]byte(publish.Args), &publishArgs); err != nil {
			return false
		}
	}
	var endpointSettings EndpointSettings
	var publishSettings EndpointSettings
	if endpoint.Settings != "" {
		if err := utils.FromJson(endpoint.Settings, &endpointSettings); err != nil {
			return false
		}
	}
	if publish.Settings != "" {
		if err := utils.FromJson(publish.Settings, &publishSettings); err != nil {
			return false
		}
	}
	return endpoint.ID == publish.EndpointId &&
		endpoint.DataappId == publish.DataappId &&
		endpoint.Type == publish.Type &&
		endpoint.Endpoint == publish.Endpoint &&
		endpoint.Method == publish.Method &&
		endpoint.ClusterId == publish.ClusterId &&
		// endpoint.Database == publish.Database &&
		endpoint.SqlTemplate == publish.SqlTemplate &&
		endpointArgs.Equal(publishArgs) &&
		endpointSettings.Equal(publishSettings) &&
		endpoint.RWMode == publish.RWMode &&
		endpoint.IsBatch() == publish.IsBatch() &&
		endpoint.Tag == publish.Tag
}

func (endpoint *Endpoint) DifffDetail(publish *EndpointPublish) (map[string]interface{}, error) {
	var endpointArgs HttpEndpointArgsList
	var publishArgs HttpEndpointArgsList
	if endpoint.Args != "" {
		if err := json.Unmarshal([]byte(endpoint.Args), &endpointArgs); err != nil {
			return nil, err
		}
	}
	if publish.Args != "" {
		if err := json.Unmarshal([]byte(publish.Args), &publishArgs); err != nil {
			return nil, err
		}
	}
	result := make(map[string]interface{})
	result["id"] = utils.CompareDiff(endpoint.ID, publish.EndpointId)
	result["dataapp_id"] = utils.CompareDiff(endpoint.DataappId, publish.DataappId)
	result["type"] = utils.CompareDiff(endpoint.Type, publish.Type)
	result["endpoint"] = utils.CompareDiff(endpoint.Endpoint, publish.Endpoint)
	result["method"] = utils.CompareDiff(endpoint.Method, publish.Method)
	result["cluster_id"] = utils.CompareDiff(endpoint.ClusterId, publish.ClusterId)
	result["database"] = utils.CompareDiff(endpoint.Database, publish.Database)
	result["sql_template"] = utils.CompareDiff(endpoint.SqlTemplate, publish.SqlTemplate)
	result["args"] = endpointArgs.DiffDetail(publishArgs)
	result["settings"] = utils.CompareDiff(endpoint.Settings, publish.Settings)
	result["rwmode"] = utils.CompareDiff(endpoint.RWMode, publish.RWMode)
	result["batch"] = utils.CompareBoolDiff(endpoint.IsBatch(), publish.IsBatch())
	result["tag"] = utils.CompareDiff(endpoint.Tag, publish.Tag)
	return result, nil
}

func (endpoint1 *Endpoint) Equal(endpoint2 *Endpoint) bool {
	var endpoint1Args HttpEndpointArgsList
	var endpoint2Args HttpEndpointArgsList
	if endpoint1.Args != "" {
		if err := json.Unmarshal([]byte(endpoint1.Args), &endpoint1Args); err != nil {
			return false
		}
	}
	if endpoint2.Args != "" {
		if err := json.Unmarshal([]byte(endpoint2.Args), &endpoint2Args); err != nil {
			return false
		}
	}
	var endpoint1Settings EndpointSettings
	var endpoint2Settings EndpointSettings
	if endpoint1.Settings != "" {
		if err := utils.FromJson(endpoint1.Settings, &endpoint1Settings); err != nil {
			return false
		}
	}
	if endpoint2.Settings != "" {
		if err := utils.FromJson(endpoint2.Settings, &endpoint2Settings); err != nil {
			return false
		}
	}
	return endpoint1.ID == endpoint2.ID &&
		endpoint1.DataappId == endpoint2.DataappId &&
		endpoint1.Name == endpoint2.Name &&
		endpoint1.Description == endpoint2.Description &&
		endpoint1.Type == endpoint2.Type &&
		endpoint1.Endpoint == endpoint2.Endpoint &&
		endpoint1.Method == endpoint2.Method &&
		endpoint1.ClusterId == endpoint2.ClusterId &&
		endpoint1.Database == endpoint2.Database &&
		endpoint1.SqlTemplate == endpoint2.SqlTemplate &&
		endpoint1Args.Equal(endpoint2Args) &&
		endpoint1Settings.Equal(endpoint2Settings) &&
		endpoint1.RWMode == endpoint2.RWMode &&
		endpoint1.IsBatch() == endpoint2.IsBatch() &&
		endpoint1.Tag == endpoint2.Tag
}

func (endpoint1 *Endpoint) IsBatch() bool {
	if endpoint1 == nil {
		return false
	}
	if endpoint1.Method == http.MethodDelete {
		return false
	}
	return endpoint1.Batch
}

func (endpoint *Endpoint) IsChat2QueryEndpoint() bool {
	if endpoint == nil {
		return false
	}
	return endpoint.Type == EndpointTypeChat2Data ||
		endpoint.Type == EndpointTypeChat2EDA
}

func (endpoint *Endpoint) IsSystemDataEndpoint() bool {
	if endpoint == nil {
		return false
	}
	return endpoint.Type == EndpointTypeSystemData
}

func (endpoint *Endpoint) GetParams() []EndpointArgs {
	var args []EndpointArgs
	if endpoint.Args != "" {
		json.Unmarshal([]byte(endpoint.Args), &args)
	}
	return args
}

func (endpoint *Endpoint) GetSettings() *EndpointSettings {
	if endpoint.Settings == "" {
		return nil
	}
	var settings EndpointSettings
	json.Unmarshal([]byte(endpoint.Settings), &settings)
	return &settings
}

func IsSystemDataEndpoint(endpointType string) bool {
	return endpointType == EndpointTypeSystemData
}

func (endpoint *Endpoint) Key() string {
	if endpoint == nil {
		return ""
	}
	return fmt.Sprintf("%s:%s", endpoint.Method, endpoint.Endpoint)
}

type DeploymentStatus struct {
	Message      string                     `json:"message"`
	ButtonStatus ButtonType                 `json:"button_status"`
	ActionType   ActionType                 `json:"action_type"`
	ActionTime   utils.JsonTime             `json:"action_time"`
	Origin       DataAppDeploymentLogOrigin `json:"origin"`
}

type EndpointExecRecord struct {
	DataAppID  uint64 `json:"dataapp_id" gorm:"column:dataapp_id"`
	EndpointID uint64 `json:"endpoint_id" gorm:"column:endpoint_id"`
	PublishID  uint64 `json:"publish_id" gorm:"column:publish_id"`
	Type       string `json:"type" gorm:"column:type"`
	Region     string `json:"region" gorm:"column:region"`
	Caller     string `json:"caller" gorm:"column:caller"`
	Status     int8   `json:"status" gorm:"column:status"`
	Channel    string `json:"channel" gorm:"column:channel"`

	Code      int64          `json:"code" gorm:"column:code"`
	Message   string         `json:"message" gorm:"column:message"`
	StartMs   int64          `json:"start_ms" gorm:"column:start_ms"`
	EndMs     int64          `json:"end_ms" gorm:"column:end_ms"`
	Latency   string         `json:"latency" gorm:"column:latency"`
	AiLatency string         `json:"ai_latency" gorm:"column:ai_latency"`
	RowCount  int64          `json:"row_count" gorm:"column:row_count"`
	RowAffect int64          `json:"row_affect" gorm:"column:row_affect"`
	Limit     uint64         `json:"limit" gorm:"column:limit"`
	TraceID   string         `json:"trace_id" gorm:"column:trace_id"`
	IsDraft   bool           `json:"is_draft" gorm:"column:is_draft"`
	CreatedAt utils.JsonTime `json:"created_at" gorm:"column:created_at"`
}

type DataUsageQueryParam struct {
	Year      int    `form:"year"`
	Month     int    `form:"month" `
	Type      string `form:"type" `
	Class     string `form:"class" `
	Region    string `form:"region" `
	DataAppId uint64 `form:"dataapp_id"`
	Start     int64  `form:"start" `
	End       int64  `form:"end"`
	Step      int    `form:"step"`
}
type MetricO11Y struct {
	AppKey           string `json:"appKey"`
	ExportedEndpoint string `json:"exported_endpoint"`
}
type ResultO11Y struct {
	Values [][]interface{} `json:"values"`
	Metric MetricO11Y      `json:"metric"`
}
type DataO11Y struct {
	ResultType string       `json:"resultType"`
	Result     []ResultO11Y `json:"result"`
}
type O11YResp struct {
	Status    string   `json:"status"`
	IsPartial bool     `json:"isPartial"`
	Data      DataO11Y `json:"data"`
}
type DeployEndpointReq struct {
	DeployName string `json:"deploy_name"`
}

type CommonTask struct {
	ID         uint64         `json:"id"`
	TaskType   string         `json:"task_type"`
	EventID    uint64         `json:"event_id"`
	Content    string         `json:"content"`
	CostMS     int            `json:"cost_ms"`
	Status     string         `json:"status"`
	ErrMessage string         `json:"err_message"`
	RetryTimes int            `json:"retry_times"`
	NextRetry  int            `json:"next_retry"`
	CreatedBy  string         `json:"created_by" gorm:"<-:create"`
	CreatedAt  utils.JsonTime `json:"created_at" gorm:"<-:create"`
	UpdatedAt  utils.JsonTime `json:"updated_at"`
	ParentID   uint64         `json:"parent_id"`
}

func (t *CommonTask) Done() bool {
	return t.Status == CommonTaskStatusSucceed || t.Status == CommonTaskStatusFailed
}

func (t *CommonTask) UnmarshalContent(target any) error {
	if t.Content == "" {
		return nil
	}
	return json.Unmarshal([]byte(t.Content), target)
}

type SampleDataTaskContent struct {
	States []SampleDataTaskRecord `json:"states"`
}

type SampleDataTaskRecord struct {
	State     string         `json:"state"` // import_data,create_endpoint,deploy_endpoint,success,fail
	StartTime utils.JsonTime `json:"start_time"`
	Message   string         `json:"message,omitempty"`
}

type AnalysisIntKVResult struct {
	Key   int64
	Value int64
}

type AnalysisStrKVResult struct {
	Key   string
	Value int64
}

type AnalysisStrStrKKVResult struct {
	Key1  string
	Key2  string
	Value int64
}

type AnalysisStrIntKKVResult struct {
	Key1  string
	Key2  int64
	Value int64
}

type EtlEndpointRegion struct {
	DataappID  uint64
	EndpointID uint64
	Region     string
	CreatedAt  utils.JsonTime
	UpdatedAt  utils.JsonTime
}

type EtlCombinedData struct {
	JobType         string         `gorm:"size:50;not null"`
	RecordTimestamp utils.JsonTime `gorm:"not null"`
	Key1            string         `gorm:"size:100"`
	Key2            string         `gorm:"size:100"`
	Key3            string         `gorm:"size:100"`
	Key4            string         `gorm:"size:100"`
	Value           float64        `gorm:"type:double"`
}

type EtlActiveUser struct {
	Email     string         `gorm:"column:email;type:varchar(100);not null;unique_index:idx_email"`
	EmailType string         `gorm:"column:email_type"`
	AppType   string         `gorm:"column:app_type"`
	CreatedAt utils.JsonTime `gorm:"column:created_at"`
	UpdatedAt utils.JsonTime `gorm:"column:updated_at"`
}

type EtlActiveUserCountInput struct {
	CreatedRange    bool
	UpdatedRange    bool
	CreatedStartSec int64
	CreatedEndSec   int64
	UpdatedStartSec int64
	UpdatedEndSec   int64
}

const (
	SampleDataTaskStateImportData     = "import_data"
	SampleDataTaskStateCreateEndpoint = "create_endpoint"
	SampleDataTaskStateDeployEndpoint = "deploy_endpoint"
	SampleDataTaskStateSuccee         = "success"
	SampleDataTaskStateFaile          = "fail"
)

type UserEndpointPlayground struct {
	ID         uint64         `json:"id"`
	DataappID  uint64         `json:"dataapp_id"`
	EndpointID uint64         `json:"endpoint_id"`
	CreatedBy  string         `json:"created_by"`
	Content    string         `json:"content"`
	CreatedAt  utils.JsonTime `json:"created_at" gorm:"<-:created_at"`
	UpdatedAt  utils.JsonTime `json:"updated_at"`
}

type EndpointPlaygroundContent struct {
	ClusterID   uint64 `json:"cluster_id"`
	Database    string `json:"database"`
	SqlTemplate string `json:"sql_template"`
}

func (c *EndpointPlaygroundContent) JsonMarshal() string {
	bytes, _ := json.Marshal(c)
	return string(bytes)
}

func (c *EndpointPlaygroundContent) JsonUnmarshal(content string) error {
	return json.Unmarshal([]byte(content), c)
}
