package platform

import (
	"fmt"
	"net/http"
	"strconv"

	"github.com/gavv/httpexpect/v2"
	"github.com/stretchr/testify/assert"
	"github.com/tidbcloud/qa-PlatformAPIAuto/common"
	"github.com/tidbcloud/qa-gourd/ProductService"
	"github.com/tidbcloud/qa-gourd/util"
)

func RunChat2QueryBasicCheckSceneTest(w *ProductService.WebRequestInfo, UrlInfoMap map[string]ProductService.UrlInfo, reporter *util.AllureReporter) {
	pt := &PlatformTest{}
	pt.Init(w, UrlInfoMap, reporter)
	pt.PrepareClusterDataScene()
	pt.CheckMeta()
	pt.CheckSettings()
	pt.CheckSqlFile()
}

func (pt *PlatformTest) CheckMeta() {
	ct := &Chat2QueryTest{
		pt:       pt,
		Provider: pt.w.Cluster.Provider,
		Region:   pt.w.Cluster.Region,
	}
	ct.pt.Reporter.StepReport("CheckMeta",
		func() {
			//get brief
			pathparam := map[string]string{
				"org_id":     ct.pt.w.User.OrgId,
				"project_id": ct.pt.w.User.ProjectId,
				"cluster_id": ct.pt.w.Cluster.ClusterId,
			}
			var getBriefResp *httpexpect.Response
			ct.pt.Reporter.StepReport("CheckBriefMeta",
				func() {
					param := map[string]string{"isbrief": "true"}
					getBriefResp = ct.pt.w.DoWebAPIRequestByUrlInfo(ct.pt.URLInfoMap["getSchema"], nil, pathparam, param)
					ct.pt.Reporter.StepReport(fmt.Sprintf("get all schema list without columns, path param is %s, status code is %d,internal code is %.0f (expect code is 200). response data is : %v (should not have columns)",
						pathparam,
						getBriefResp.Raw().StatusCode,
						getBriefResp.JSON().Path(CodePath).Number().Raw(),
						getBriefResp.JSON().Raw(),
					),
						func() {
							getBriefResp.Status(http.StatusOK)
							getBriefResp.JSON().Object().HasValue("code", 200)
							common.CheckHasColumn(getBriefResp.JSON().Path("$.data").Array(), false)
						})
				})

			//get no brief
			var getNoBriefResp *httpexpect.Response
			ct.pt.Reporter.StepReport("CheckNoBriefMeta",
				func() {
					param := map[string]string{"isbrief": "false"}
					getNoBriefResp = ct.pt.w.DoWebAPIRequestByUrlInfo(ct.pt.URLInfoMap["getSchema"], nil, pathparam, param)
					ct.pt.Reporter.StepReport(fmt.Sprintf("get all schema list, path param is %s, status code is %d,internal code is %.0f (expect code is 200) , resp is : %v (should include columns)",
						pathparam,
						getNoBriefResp.Raw().StatusCode,
						getNoBriefResp.JSON().Path(CodePath).Number().Raw(),
						getNoBriefResp.JSON().Raw(),
					),
						func() {
							getNoBriefResp.Status(http.StatusOK)
							getNoBriefResp.JSON().Object().HasValue("code", 200)
							common.CheckHasColumn(getNoBriefResp.JSON().Path("$.data").Array(), true)
						})
				})

			//check db schema
			ct.pt.Reporter.StepReport("Check DB Schema",
				func() {
					for _, db := range getBriefResp.JSON().Path("$.data").Array().Iter() {
						findDB := false
						dbName := db.Path("$.name").String().Raw()
						for _, val := range getNoBriefResp.JSON().Path("$.data").Array().Iter() {
							if val.Path("$.name").String().Raw() == dbName {
								findDB = true
								for _, table := range val.Path("$.tables").Array().Iter() {
									ct.pt.Reporter.StepReport("CheckTableSchema",
										func() {
											ct.CheckTableSchema(dbName, table)
										})
								}
								break
							}
						}
						value := httpexpect.NewBoolean(ct.pt.Reporter.T, findDB)
						ct.pt.Reporter.StepReport(fmt.Sprintf("Brief and no brief meta consistency is : %v ", findDB),
							func() {
								value.IsEqual(true)
							})
					}
				})

			if ct.pt.w.Cluster.IsServerless {
				ct.DB, ct.Table = common.GetSpecTable(getBriefResp, Base_Table)
			}
		})
}

func (pt *PlatformTest) CheckSettings() {
	ct := &Chat2QueryTest{
		pt:       pt,
		Provider: pt.w.Cluster.Provider,
		Region:   pt.w.Cluster.Region,
	}
	ct.pt.Reporter.StepReport("CheckSettings",
		func() {
			pathparam := map[string]string{
				"org_id":     ct.pt.w.User.OrgId,
				"project_id": ct.pt.w.User.ProjectId,
				"cluster_id": ct.pt.w.Cluster.ClusterId,
			}
			// update settings
			ct.pt.Reporter.StepReport("Update sql editor settings",
				func() {
					body := map[string]int{"limit": 1000}
					updateSettingsResp := ct.pt.w.DoWebAPIRequestByUrlInfo(ct.pt.URLInfoMap["updateSqlSettings"], body, pathparam, nil)
					ct.pt.Reporter.StepReport(fmt.Sprintf("update sql editor settings to set limit, path param is %s, status code is %d",
						pathparam,
						updateSettingsResp.Raw().StatusCode),
						func() {
							updateSettingsResp.Status(http.StatusOK)
						})
				})

			// check settings
			ct.pt.Reporter.StepReport("Check sql editor settings",
				func() {
					getSettingsResp := ct.pt.w.DoWebAPIRequestByUrlInfo(ct.pt.URLInfoMap["getSqlSettings"], nil, pathparam, nil)
					ct.pt.Reporter.StepReport(fmt.Sprintf("get sql editor settings, path param is %s, status code is %d, Expect limit row is : 1000 , actual limit row is %v",
						pathparam,
						getSettingsResp.Raw().StatusCode,
						getSettingsResp.JSON().Path("$.data.limit").Number().Raw()),
						func() {
							getSettingsResp.Status(http.StatusOK)
							getSettingsResp.JSON().Path("$.data.limit").Number().IsEqual(1000)
						})
				})

			// check limit can't be greater than 2001
			ct.pt.Reporter.StepReport("Check sql editor settings (limit can not be greater than 2001)",
				func() {
					body := map[string]int{"limit": 2001}
					updateSettingsResp := ct.pt.w.DoWebAPIRequestByUrlInfo(ct.pt.URLInfoMap["updateSqlSettings"], body, pathparam, nil)

					ct.pt.Reporter.StepReport(fmt.Sprintf("update sql editor settings, path param is %s, status code is %d, internal code is %.0f (we expect internal code is 49900001)",
						pathparam,
						updateSettingsResp.Raw().StatusCode,
						updateSettingsResp.JSON().Path(CodePath).Number().Raw()),
						func() {
							updateSettingsResp.Status(http.StatusBadRequest)
							updateSettingsResp.JSON().Path("$.code").Number().IsEqual(49900001)
						})
				})

			// reset settings
			ct.pt.Reporter.StepReport("Check sql editor settings (limit set to 500)",
				func() {
					body := map[string]int{"limit": 500}
					updateSettingsResp := ct.pt.w.DoWebAPIRequestByUrlInfo(ct.pt.URLInfoMap["updateSqlSettings"], body, pathparam, nil)
					ct.pt.Reporter.StepReport(fmt.Sprintf("reset sql editor settings to original, path param is %s, status code is %d", pathparam, updateSettingsResp.Raw().StatusCode),
						func() {
							updateSettingsResp.Status(http.StatusOK)
						})
				})

			//check settings
			ct.pt.Reporter.StepReport("Get sql editor settings (limit set to 500)",
				func() {
					getSettingsResp := ct.pt.w.DoWebAPIRequestByUrlInfo(ct.pt.URLInfoMap["getSqlSettings"], nil, pathparam, nil)
					ct.pt.Reporter.StepReport(fmt.Sprintf("get sql editor settings, path param is %s, status code is %d,Expect limit row is : 500 , actual limit row is %v ",
						pathparam,
						getSettingsResp.Raw().StatusCode,
						getSettingsResp.JSON().Path("$.data.limit").Number().Raw(),
					),
						func() {
							getSettingsResp.Status(http.StatusOK)
							getSettingsResp.JSON().Path("$.data.limit").Number().IsEqual(500)
						})
				})
			// show system schema
			ct.pt.Reporter.StepReport("Show system schema",
				func() {
					body := map[string]bool{"show_system_db_schema": true}
					updateSettingsResp := ct.pt.w.DoWebAPIRequestByUrlInfo(ct.pt.URLInfoMap["updateSqlSettings"], body, pathparam, nil)
					param := map[string]string{"isbrief": "true"}
					getBriefResp := ct.pt.w.DoWebAPIRequestByUrlInfo(ct.pt.URLInfoMap["getSchema"], nil, pathparam, param)
					res := common.CheckHasSchema(getBriefResp.JSON().Path("$.data").Array(), SystemSchema)
					ct.pt.Reporter.StepReport(fmt.Sprintf("show system db schema, status code is %d ,has system db result is : %v",
						updateSettingsResp.Raw().StatusCode,
						res),
						func() {
							updateSettingsResp.Status(http.StatusOK)
							assert.True(ct.pt.Reporter.T, res)
						})
				})

			// dont show system schema
			ct.pt.Reporter.StepReport("No system schema",
				func() {
					body := map[string]bool{"show_system_db_schema": false}
					updateSettingsResp := ct.pt.w.DoWebAPIRequestByUrlInfo(ct.pt.URLInfoMap["updateSqlSettings"], body, pathparam, nil)
					param := map[string]string{"isbrief": "true"}
					getBriefResp := ct.pt.w.DoWebAPIRequestByUrlInfo(ct.pt.URLInfoMap["getSchema"], nil, pathparam, param)
					res := common.CheckHasSchema(getBriefResp.JSON().Path("$.data").Array(), SystemSchema)
					ct.pt.Reporter.StepReport(fmt.Sprintf("No system db schema, status code is %d ,has system db result is : %v",
						updateSettingsResp.Raw().StatusCode,
						res),
						func() {
							updateSettingsResp.Status(http.StatusOK)
							assert.False(ct.pt.Reporter.T, res)
						})
				})
		})

}

func (pt *PlatformTest) CheckSqlFile() {
	ct := &Chat2QueryTest{
		pt:       pt,
		Provider: pt.w.Cluster.Provider,
		Region:   pt.w.Cluster.Region,
	}
	ct.pt.Reporter.StepReport("CheckSqlFile",
		func() {
			pathparam := map[string]string{
				"org_id":     ct.pt.w.User.OrgId,
				"project_id": ct.pt.w.User.ProjectId,
				"cluster_id": ct.pt.w.Cluster.ClusterId,
			}
			// add sql file
			var sqlFileId string
			ct.pt.Reporter.StepReport("Add sql file",
				func() {
					body := map[string]string{"name": FileName, "content": ""}
					addSqlFileResp := ct.pt.w.DoWebAPIRequestByUrlInfo(ct.pt.URLInfoMap["addSqlFile"], body, pathparam, nil)
					ct.pt.Reporter.StepReport(fmt.Sprintf("create an empty sql file, path param is %s, body is"+
						" %s, status code is %d, fileId is %1.0f",
						pathparam,
						body,
						addSqlFileResp.Raw().StatusCode,
						addSqlFileResp.JSON().Path("$.data").Number().Raw()),
						func() {
							addSqlFileResp.Status(http.StatusOK)
							sqlFileId = fmt.Sprintf("%1.0f", addSqlFileResp.JSON().Path("$.data").Number().Raw())
						})
				})
			// update sql file
			FilePathParam := map[string]string{
				"org_id":     ct.pt.w.User.OrgId,
				"project_id": ct.pt.w.User.ProjectId,
				"cluster_id": ct.pt.w.Cluster.ClusterId,
				"file_id":    sqlFileId,
			}
			ct.pt.Reporter.StepReport("Update sql file",
				func() {
					body := map[string]string{
						"name":    FileName,
						"content": FileSQL,
					}
					updateSqlFileResp := ct.pt.w.DoWebAPIRequestByUrlInfo(ct.pt.URLInfoMap["updateSqlFile"], body, FilePathParam, nil)
					ct.pt.Reporter.StepReport(fmt.Sprintf("update sql file's sql content, path param is %+v, body is"+
						" %+v, status code is %d, resp message is : %s",
						FilePathParam,
						body,
						updateSqlFileResp.Raw().StatusCode,
						updateSqlFileResp.JSON().Path(MessagePath).String().Raw()),
						func() {
							updateSqlFileResp.Status(http.StatusOK)
							updateSqlFileResp.JSON().Path("$.message").String().IsEqual("ok")
						})
				})

			// check sql file content
			ct.pt.Reporter.StepReport("Check sql file content",
				func() {
					getSqlFileResp := ct.pt.w.DoWebAPIRequestByUrlInfo(ct.pt.URLInfoMap["getSqlFileById"], nil, FilePathParam, nil)
					ct.pt.Reporter.StepReport(fmt.Sprintf("get sql file, path param is %+v, status code is %d, file id is : %v , file name is : %s (should be %s), sql content is : %s (should be %s) ",
						FilePathParam,
						getSqlFileResp.Raw().StatusCode,
						getSqlFileResp.JSON().Path("$.data.id").Number().Raw(),
						getSqlFileResp.JSON().Path("$.data.name").String().Raw(),
						FileName,
						getSqlFileResp.JSON().Path("$.data.content").String().Raw(),
						FileSQL,
					),
						func() {
							getSqlFileResp.Status(http.StatusOK)
							id := fmt.Sprintf("%1.0f", getSqlFileResp.JSON().Path("$.data.id").Number().Raw())
							assert.Equal(ct.pt.Reporter.T, id, sqlFileId)
							getSqlFileResp.JSON().Path("$.data.name").String().IsEqual(FileName)
							getSqlFileResp.JSON().Path("$.data.content").String().IsEqual(FileSQL)
						})
				})

			//delete sql file
			ct.pt.Reporter.StepReport("Delete sql file",
				func() {
					deleteSqlFileResp := ct.pt.w.DoWebAPIRequestByUrlInfo(ct.pt.URLInfoMap["delSqlFile"], nil, FilePathParam, nil)
					ct.pt.Reporter.StepReport(fmt.Sprintf("delete sql file, path param is %s, status code is %d,resp message is : %s",
						FilePathParam,
						deleteSqlFileResp.Raw().StatusCode,
						deleteSqlFileResp.JSON().Path(MessagePath).String().Raw(),
					),
						func() {
							deleteSqlFileResp.Status(http.StatusOK)
							deleteSqlFileResp.JSON().Path("$.message").String().IsEqual("ok")
						})
				})

			// check sql file list
			ct.pt.Reporter.StepReport("Delete sql file",
				func() {
					getSqlFileListResp := ct.pt.w.DoWebAPIRequestByUrlInfo(ct.pt.URLInfoMap["getSqlFiles"], nil, pathparam, nil)
					ct.pt.Reporter.StepReport(fmt.Sprintf("get sql file list, to check whether deleted file is in file list, path param is %s, status code is %d, resp is : %+v.",
						pathparam,
						getSqlFileListResp.Raw().StatusCode,
						getSqlFileListResp.JSON().Path("$.data").Array().Raw(),
					),
						func() {
							getSqlFileListResp.Status(http.StatusOK)
							// check whether deleted file is in file list
							find := false
							file_Id_string, _ := strconv.ParseFloat(sqlFileId, 64)
							for _, val := range getSqlFileListResp.JSON().Path("$.data").Array().Iter() {
								if val.Path("$.id").Number().Raw() == file_Id_string {
									find = true
									break
								}
							}
							value := httpexpect.NewBoolean(ct.pt.Reporter.T, find)
							value.IsEqual(false)
						})
				})
		})
}

func (ct *Chat2QueryTest) CheckTableSchema(db string, table httpexpect.Value) {

	tableName := table.Path("$.name").String().Raw()
	pathParam := map[string]string{
		"org_id":     ct.pt.w.User.OrgId,
		"project_id": ct.pt.w.User.ProjectId,
		"cluster_id": ct.pt.w.Cluster.ClusterId,
		"db_name":    db,
		"table_name": tableName,
	}

	// get table list
	getTableSchemaResp := ct.pt.w.DoWebAPIRequestByUrlInfo(ct.pt.URLInfoMap["getTableSchema"], nil, pathParam, nil)
	ct.pt.Reporter.StepReport(fmt.Sprintf("get table schema, db is %s, table is %v", db, table),
		func() {
			getTableSchemaResp.Status(http.StatusOK)
			for n, col := range getTableSchemaResp.JSON().Path("$.data.columns").Array().Iter() {
				col.Object().Value("col").String().IsEqual(table.Path("$.columns").Array().Value(n).Object().Value("col").String().Raw())
				col.Object().Value("data_type").String().IsEqual(table.Path("$.columns").Array().Value(n).Object().Value("data_type").String().Raw())
				col.Object().Value("nullable").Boolean().IsEqual(table.Path("$.columns").Array().Value(n).Object().Value("nullable").Boolean().Raw())
			}
		})
}
