package cluster

import (
	"database/sql"
	"fmt"
	"testing"

	l4g "github.com/alecthomas/log4go"
	"github.com/dailymotion/allure-go"
	"github.com/tidbcloud/qa-PlatformAPIAuto/common"
	"github.com/tidbcloud/qa-PlatformAPIAuto/models"
	sqlpkg "github.com/tidbcloud/qa-PlatformAPIAuto/webScene/pkg/sql"
)

type DedicatedCluster struct {
	*common.DbassRequestInfo

	T *testing.T

	endPoint *models.CentralEndpoint
}

func (d *DedicatedCluster) Id() string {
	return d.ClusterId
}

func (d *DedicatedCluster) SetId(id string) {
	d.ClusterId = id
}

func (d *DedicatedCluster) Create(provider string) {
	if d.ClusterId != "" {
		l4g.Info("tidb cluster already exists")
		d.updatePassword(DefaultPassword)
		d.addTraffic()
		return
	}
	d.ClusterId = common.CheckCreateCluster(
		d.T,
		provider,
		false,
		defaultDedicatedTidbCount,
		defaultDedicatedTikvCount,
		defaultDedicatedTiflashCount,
		"", "", "", "",
	)
}

func (d *DedicatedCluster) Execute(dataFunc sqlpkg.Statement) {
	db, err := d.getConnection()
	defer db.Close()
	if err != nil {
		errorMsg := fmt.Errorf("connect to tidb occur error: %s", err)
		l4g.Error(errorMsg)
		allure.FailNow(errorMsg)
	}

	err = dataFunc(db)
	if err != nil {
		errorMsg := fmt.Errorf("executed tidb data occur error: %s", err)
		l4g.Error(errorMsg)
		allure.FailNow(errorMsg)
	}
}

func (d *DedicatedCluster) getConnection() (*sql.DB, error) {
	pathParam := map[string]string{
		"org_id":     d.CommonEnv.OrgId,
		"project_id": d.CommonEnv.ProjectId,
		"cluster_id": d.ClusterId,
	}

	if d.endPoint == nil {
		callResp := d.DbassRequest(d.T, "getClusterInfo", nil, pathParam, nil, nil)
		httpInfo := common.ParseHttpInfo(callResp)
		if httpInfo.RespStatusCode != common.StatusOK {
			errorMsg := fmt.Errorf("status code is: %d, http details is: %s", httpInfo.RespStatusCode, httpInfo.ToString())
			l4g.Error(errorMsg)
			allure.FailNow(errorMsg)
		}

		clusterResp := &models.CentralGetClusterResp{}
		httpInfo.UnmarshalRespBody(clusterResp)

		d.endPoint = clusterResp.Cluster.Endpoint
	}

	tidbAddress := d.endPoint.Address
	tidbPort := d.endPoint.Port

	return common.ConnectTidbUseTLS(*tidbAddress, (int64)(*tidbPort), DefaultPassword, "test", "root")
}

func (d *DedicatedCluster) GetDataIngestionProfile() *models.DataflowDataIngestionProfile {
	if d.ClusterId == "" {
		errorMsg := fmt.Errorf("cluster id is empty")
		l4g.Error(errorMsg)
		allure.FailNow(errorMsg)
	}
	pathParam := map[string]string{
		"org_id":     d.CommonEnv.OrgId,
		"project_id": d.CommonEnv.ProjectId,
		"cluster_id": d.ClusterId,
	}

	callResp := d.DbassRequest(d.T, "getDataIngestionProfile", nil, pathParam, nil, nil)
	httpInfo := common.ParseHttpInfo(callResp)
	if httpInfo.RespStatusCode != common.StatusOK {
		errorMsg := fmt.Errorf("status code is: %d, http details is: %s", httpInfo.RespStatusCode, httpInfo.ToString())
		l4g.Error(errorMsg)
		allure.FailNow(errorMsg)
	}
	dataIngestionProfile := &models.DataflowDataIngestionProfile{}
	httpInfo.UnmarshalRespBody(dataIngestionProfile)

	return dataIngestionProfile
}

func (d *DedicatedCluster) updatePassword(password string) {
	if d.ClusterId == "" {
		errorMsg := fmt.Errorf("cluster id is empty")
		l4g.Error(errorMsg)
		allure.FailNow(errorMsg)
	}
	pathParam := map[string]string{
		"org_id":     d.CommonEnv.OrgId,
		"project_id": d.CommonEnv.ProjectId,
		"cluster_id": d.ClusterId,
	}

	body := map[string]string{"password": password}
	callResp := d.DbassRequest(d.T, "updatePassword", body, pathParam, nil, nil)

	httpInfo := common.ParseHttpInfo(callResp)
	if httpInfo.RespStatusCode != common.StatusOK {
		errorMsg := fmt.Errorf("status code is: %d, http details is: %s", httpInfo.RespStatusCode, httpInfo.ToString())
		l4g.Error(errorMsg)
		allure.FailNow(errorMsg)
	}
}

func (d *DedicatedCluster) addTraffic() {
	pathParam := map[string]string{
		"org_id":     d.CommonEnv.OrgId,
		"project_id": d.CommonEnv.ProjectId,
		"cluster_id": d.ClusterId,
	}
	filter := common.NewTrafficFilter(d.CommonEnv.Cicr, "", false)
	callResp := d.DbassRequest(d.T, "addFilter", filter, pathParam, nil, nil)

	httpInfo := common.ParseHttpInfo(callResp)
	if httpInfo.RespStatusCode != common.StatusOK {
		errorMsg := fmt.Errorf("status code is: %d, http details is: %s", httpInfo.RespStatusCode, httpInfo.ToString())
		l4g.Error(errorMsg)
		allure.FailNow(errorMsg)
	}
}
