package tiup_upgrade

import (
	"database/sql"
	"fmt"
	"net/url"
	"time"

	_ "github.com/go-sql-driver/mysql"
	. "github.com/pingcap/endless/pkg/matcher"
	. "github.com/pingcap/endless/pkg/workloadnode/tiup"
	"github.com/pingcap/log"
	"github.com/pingcap/test-infra/sdk/resource"
	_ "github.com/pingcap/test-infra/sdk/resource/impl/k8s" // register k8s impl

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	_ "github.com/pingcap/endless/pkg/client/tidb"
	"go.uber.org/zap"
)

var tiupTopo = `
# # Global variables are applied to all deployments and used as the default value of
# # the deployments if a specific deployment value is missing.
global:
  user: "tidb-test"
  ssh_port: 22
  deploy_dir: "/tiup/deploy"
  data_dir: "/tiup/data"
  arch: "amd64"
monitored:
  node_exporter_port: 19100
  blackbox_exporter_port: 19115
server_configs:
  tidb:
    log.level: "debug"
    binlog.enable: true
    binlog.ignore-error: false
    alter-primary-key: true
    #enable-telemetry: true
    new_collations_enabled_on_first_bootstrap: true
    experimental.allow-expression-index: false
    #graceful-wait-before-shutdown: 1
    oom-action: cancel
    oom-use-tmp-storage: false
    #pd-client.pd-server-timeout: 5
    performance.committer-concurrency: 16
    performance.feedback-probability: 0.0
    #performance.gogc: 80
    performance.max-memory: 5000
    performance.max-txn-ttl: 600000
    performance.query-feedback-limit: 1024
    prepared-plan-cache.enabled: true
    #stmt-summary.enable-internal-query: true
    #tikv-client.async-commit.keys-limit: 256
    #tikv-client.async-commit.total-key-size-limit: 4096
    #tikv-client.copr-cache.admission-max-ranges: 500
    tikv-client.copr-cache.admission-max-result-mb: 10.0
    tikv-client.copr-cache.admission-min-process-ms: 5
    tikv-client.copr-cache.capacity-mb: 100.0
    tikv-client.enable-chunk-rpc: false
    tikv-client.store-liveness-timeout: 2s
    #security.auto-tls: true
  pd:
    enable-local-tso: false
    log.level: "INFO"
    enable-placement-rules: true
    enable-redact-log: true
    initial-cluster-token: pd-cluster
    replication.enable-placement-rules: false
    replication.isolation-level: ""
    replication.location-labels:
    - zone
    - dc
    - host
    replication.max-replicas: 2
    schedule.low-space-ratio: 0.9
    server.schedule.store-balance-rate: 16
    tso-update-physical-interval: 50ms
  pump:
    gc: 2
  tikv:
    log-level: "info"
    log-format: "text"
    log-rotation-size: "10MB"
    log-rotation-timespan: "1h"
pd_servers:
  - host: pd-peer
    client_port: 12379
    peer_port: 12380
  - host: pd2-peer
    client_port: 2379
    peer_port: 2380
  - host: pd2-peer
    client_port: 22379
    peer_port: 22380

tidb_servers:
  - host: tidb-peer
    port: 14000
    status_port: 20080
  - host: tidb-peer
    port: 4000
    status_port: 10080

tikv_servers:
  - host: tikv-peer
    port: 20160
    status_port: 20180
    config: 
      server.labels:
        zone: z1
        host: h1
  - host: tikv-peer
    port: 20260
    status_port: 20280
    config: 
      server.labels:
        zone: z1
        host: h2
  - host: tikv2-peer
    port: 20360
    status_port: 20380
    config: 
      server.labels:
        zone: z1
        host: h3
  - host: tikv2-peer
    port: 20460
    status_port: 20480
    config: 
      server.labels:
        zone: z1
        host: h4

tiflash_servers:
  - host: tiflash-peer
    tcp_port: 9000
    http_port: 8123
    config:
      storage.main.dir:
      - /tiup/data/tiflash-9000
    learner_config:
      server.labels:
        host: tiflash-peer
        zone: az1
  - host: tiup-peer
    tcp_port: 9000
    http_port: 8123
    config:
      storage.main.dir:
      - /tiup/data/tiflash-9000
    learner_config:
      server.labels:
        host: tiup-peer
        zone: az1

cdc_servers:
  - host: cdc-peer
    port: 8300

monitoring_servers:
  - host: tiup-peer
    port: 19090
    ng_port: 12020

grafana_servers:
  - host: tiup-peer
    port: 13000

alertmanager_servers:
  - host: tiup-peer
    web_port: 19093
    cluster_port: 19094
pump_servers:
  - host: tiup-peer
  - host: cdc-peer
drainer_servers:
  - host: tiflash-peer
    commit_ts: -1
    config:
        syncer.ignore-schemas: "INFORMATION_SCHEMA,PERFORMANCE_SCHEMA,mysql"
        syncer.txn-batch: 100
        syncer.worker-count: 64
        syncer.disable-dispatch: false
        syncer.safe-mode: false
        syncer.channel-id: 1
        syncer.sync-ddl: true
        syncer.relay.log-dir: "/tiup/deploy/drainer-8249"
        syncer.db-type: "mysql"
        syncer.to.host: mysql-peer
        syncer.to.user: "root"
        syncer.to.password: ""
        syncer.to.port: 3306
        syncer.to.sync-mode: 2
        syncer.ignore-table:
        - db-name: "tidb_binlog"
          tbl-name: "checkpoint"
        syncer.replicate-do-db: ["bank","downDB"]
`
var tiupTopo2 = `
# # Global variables are applied to all deployments and used as the default value of
# # the deployments if a specific deployment value is missing.
monitored:
  node_exporter_port: 19100
  blackbox_exporter_port: 19115
pd_servers:
  - host: cluster2-peer
tidb_servers:
  - host: cluster2-peer
tikv_servers:
  - host: cluster2-peer
monitoring_servers:
  - host: cluster2-peer
grafana_servers:
  - host: cluster2-peer
alertmanager_servers:
  - host: cluster2-peer
server_configs:
  tidb:
    log.level: "debug"
    alter-primary-key: true
    enable-telemetry: true
    new_collations_enabled_on_first_bootstrap: true
    experimental.allow-expression-index: false
    graceful-wait-before-shutdown: 1
    oom-action: cancel
    oom-use-tmp-storage: false
    performance.committer-concurrency: 16
    performance.feedback-probability: 0.0
    performance.gogc: 80
    performance.max-memory: 5000
    performance.max-txn-ttl: 600000
    performance.query-feedback-limit: 1024
    prepared-plan-cache.enabled: true
    #stmt-summary.enable-internal-query: true
`

func SET_TIUP_STAG_MIRROR(
	workloadNode resource.WorkloadNode,
) {
	cmd := "tiup mirror set http://172.16.5.139:8988"
	Try(workloadNode.Exec(resource.WorkloadNodeExecOptions{Command: cmd}))
	res, _, _, _ := workloadNode.Exec(resource.WorkloadNodeExecOptions{Command: "tiup mirror show"}) // 直接用
	Expect(res).Should(ContainSubstring("139"))
}

func SET_TIUP_PRODUCT_MIRROR(
	workloadNode resource.WorkloadNode,
) {
	cmd := "tiup mirror set http://172.16.5.134:8987"
	Try(workloadNode.Exec(resource.WorkloadNodeExecOptions{Command: cmd}))
	res, _, _, _ := workloadNode.Exec(resource.WorkloadNodeExecOptions{Command: "tiup mirror show"}) // 直接用
	Expect(res).Should(ContainSubstring("134"))
}

func TIUP_INSTALL_BR(
	workloadNode resource.WorkloadNode,
) {
	cmd := "tiup install br"
	Try(workloadNode.Exec(resource.WorkloadNodeExecOptions{Command: cmd}))
	res, _, _, _ := workloadNode.Exec(resource.WorkloadNodeExecOptions{Command: "tiup br --help"}) // 直接用
	Expect(res).Should(ContainSubstring("br is a TiDB/TiKV cluster backup restore tool"))
}
func TIUP_INSTALL_Cluster2(
	workloadNode resource.WorkloadNode,
) {
	targetVersion = ctx.Param("target_version")
	cluster2 = NewTiUPCluster(workloadNode, "tiup-peer", tiupTopo2, "cluster2").WithCMDTimeout(60 * time.Minute)
	must(cluster2.Deploy(targetVersion))
	must(cluster2.Start())
	info, err := cluster2.Display()
	must(err)
	log.Info("before upgrade", zap.String("current version:", info.ClusterMeta.ClusterVersion))

}

func TIUP_INSTALL_Cluster1(
	workloadNode resource.WorkloadNode,
) {
	installVersion = ctx.Param("init_version")
	cluster = NewTiUPCluster(workloadNode, "tiup-peer", tiupTopo, "debug").WithCMDTimeout(60 * time.Minute)
	must(cluster.Deploy(installVersion, "--ssh=system"))
	must(cluster.Start())
	info, err := cluster.Display()
	must(err)
	log.Info("before upgrade", zap.String("current version:", info.ClusterMeta.ClusterVersion))

}

func TIUP_UPGRADE(
	cluster *TiUPCluster,
) {
	Try(cluster.Upgrade(targetVersion, "--wait-timeout 3600 --ssh=system"))
	info1 := Try(cluster.Display()).(TiUPClusterInfo)
	log.Info("after upgrade", zap.String("current version:", info1.ClusterMeta.ClusterVersion))
	if info1.ClusterMeta.ClusterVersion != targetVersion {
		Fail("upgrade fail")
	}

	Try(cluster.ServiceURL(resource.DBAddr))
}

func TIUP_DESTYOY(
	cluster *TiUPCluster,
) {
	Try(cluster.ServiceURL(resource.DBAddr))
	Try(cluster.Destroy())
}

func TIUP_SCALE_IN(
	cluster *TiUPCluster,
	node string,
) {
	Try(cluster.ServiceURL(resource.DBAddr))
	Try(cluster.ScaleIn(node, "--wait-timeout 3600"))
}

func TIUP_SCALE_OUT(
	cluster *TiUPCluster,
	topo string,
) {
	Try(cluster.ServiceURL(resource.DBAddr))
	Try(cluster.ScaleOut(topo, "--wait-timeout 3600"))

}
func INIT_MYSQL() {
	mysqlCmd := ctx.Resource("mysql").(resource.WorkloadNode)
	mysqlstr := fmt.Sprintf(" mysql -h mysql-peer -P3306 -uroot -e 'drop database if exists tptest;'")
	Try(mysqlCmd.Exec(resource.WorkloadNodeExecOptions{Command: mysqlstr}))
	mysqlstr = fmt.Sprintf(" mysql -h mysql-peer -P3306 -uroot -e 'create database tptest;'")
	Try(mysqlCmd.Exec(resource.WorkloadNodeExecOptions{Command: mysqlstr}))

	mysqlstr = fmt.Sprintf(" mysql -h mysql-peer -P3306 -uroot -e 'drop database if exists randgen;'")
	Try(mysqlCmd.Exec(resource.WorkloadNodeExecOptions{Command: mysqlstr}))
	mysqlstr = fmt.Sprintf(" mysql -h mysql-peer -P3306 -uroot -e 'create database randgen;'")
	Try(mysqlCmd.Exec(resource.WorkloadNodeExecOptions{Command: mysqlstr}))

}
func INIT_Cluster1() {
	// cluster = host.NewTiUPCluster(workloadNode, "tiup-peer", tiupTopo, "debug").WithCMDTimeout(60 * time.Minute)
	dbURL = Try(cluster.ServiceURL(resource.DBAddr)).(*url.URL)
	tidbIp = dbURL.Hostname()
	tidbPort = dbURL.Port()
	log.Info("db info", zap.String("url", dbURL.String()), zap.String("ip:", tidbIp), zap.String("port:", tidbPort), zap.String("host:", dbURL.Host))
	db := Try(cluster.GetDB("test")).(*sql.DB)
	defer db.Close()
	Try(db.Query("drop database if exists downDB;"))
	Try(db.Query("create database downDB;"))
	Try(db.Query("drop database if exists expidx;"))
	Try(db.Query("create database expidx;"))

}

func Clinic_Upload_Config(
	mycluster *TiUPCluster,
	output string,
) {
	link, err := mycluster.Diag().WithUploadMaxSize(1500000).CollectAndUpload(DiagCollectAndUploadOptions{
		Collect: DiagCollectOptions{
			Output: output,
		},
		Upload: DiagUploadOptions{
			Token: "eyJrIjoiSHBlVjFTdnI1cHlDMG5PeSIsInUiOjMyLCJpZCI6MzN9"}})
	must(err)
	log.Info("clinic link", zap.String("url", link))
}

func TIUP_EDIT_CFG(
	mycluster *TiUPCluster,
	config string,
) {
	Try(mycluster.UpdateConfig(resource.TiDBClusterUpdateConfigOptions{
		Target:  resource.TiDB,
		Config:  config,
		Timeout: 5 * time.Minute,
	}))
	Try(mycluster.ServiceURL(resource.DBAddr))

}

func TIUP_EDIT_TiKV_CFG(
	mycluster *TiUPCluster,
	config string,
) {
	Try(mycluster.UpdateConfig(resource.TiDBClusterUpdateConfigOptions{
		Target:  resource.TiKV,
		Config:  config,
		Timeout: 5 * time.Minute,
	}))
	Try(mycluster.ServiceURL(resource.DBAddr))
}

func SET_TIKV_SECURITY() {
	tikv1 := ctx.Resource("tikv").(resource.WorkloadNode)
	tikv2 := ctx.Resource("tikv2").(resource.WorkloadNode)
	cmdstr := fmt.Sprintf("echo 603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4 > /tiup/deploy/master_key")
	Try(tikv1.Exec(resource.WorkloadNodeExecOptions{Command: cmdstr}))
	Try(tikv2.Exec(resource.WorkloadNodeExecOptions{Command: cmdstr}))

}
