package framework

import (
	"fmt"
	"io"
	"net/url"
	"time"

	"k8s.io/apimachinery/pkg/util/wait"

	apiresource "github.com/pingcap/test-infra/common/model/resource"
	"github.com/pingcap/test-infra/sdk/resource"
	"github.com/pingcap/test-infra/sdk/resource/impl/k8s"
)

func (f *Framework) CreateDrainer(testbed *k8s.Testbed) *k8s.TiDBDrainer {
	client := k8s.NewTiDBDrainerClient(f.KubeClientSetPack)

	r, err := client.Create([]byte(fmt.Sprintf(`
{
  "name": "drainer-1",
  "testbed": "%s",
  "type": "%s",
  "spec": {
    "clusterName": "tc",
    "clusterVersion": "v5.4.0",
    "baseImage": "hub.pingcap.net/qa/tidb-binlog",
    "storageClassName": "local-path",
    "storage": "10Gi",
    "initialCommitTs": "-1",
    "config": "detect-interval = 10\n[syncer]\nworker-count = 16\ntxn-batch = 20\ndisable-dispatch = false\nignore-schemas = \"INFORMATION_SCHEMA,PERFORMANCE_SCHEMA,mysql\"\nsafe-mode = false\ndb-type = \"tidb\"\n[syncer.to]\nhost = \"downstream-tidb\"\nuser = \"root\"\npassword = \"\"\nport = 4000\n",
    "resources": {"requests": {"cpu": "2000m", "memory": "4Gi"}}
  }
}
`, testbed.Name, apiresource.ResourceTypeDrainer)))

	ExpectNoError(err)
	return r.(*k8s.TiDBDrainer)
}

func (f *Framework) CreateTiDBClusterWithPumpAndDownstream(testbed *k8s.Testbed) (*k8s.TiDBCluster, *k8s.TiDBCluster) {
	client := k8s.NewTiDBClusterClient(f.KubeClientSetPack)

	r, err := client.Create([]byte(fmt.Sprintf(`{
  "name": "tc",
  "testbed": "%s",
  "type": "%s",
  "spec": {
    "pd": {
      "replicas": 1,
      "baseImage": "hub.pingcap.net/qa/pd",
      "version": "v5.4.0"
    },
    "tidb": {
      "replicas": 1,
      "baseImage": "hub.pingcap.net/qa/tidb",
      "version": "v5.4.0",
      "requests": {
        "cpu": 1,
        "memory": "1Gi"
      },
      "limits": {
        "cpu": 1,
        "memory": "1Gi"
      },
      "config": "[log]\nlevel = \"info\"\nnot-exist-config = 1"
    },
    "tikv": {
      "replicas": 1,
      "baseImage": "hub.pingcap.net/qa/tikv",
      "version": "v5.4.0",
      "requests": {
        "cpu": 1,
        "memory": "1Gi"
      },
      "limits": {
        "cpu": 1,
        "memory": "1Gi"
      },
      "evictLeaderTimeout": "10s",
      "config": "[storage]\nreserve-space = \"100MB\"\n[rocksdb]\nmax-open-files = 256"
    },
    "pump": {
      "replicas": 1,
      "baseImage": "hub.pingcap.net/qa/tidb-binlog",
      "version": "v5.4.0",
      "requests": {
        "cpu": 1,
        "memory": "1Gi",
        "storage": "30Gi"
      },
      "config": "addr = \"0.0.0.0:8250\"\ngc = 7\nheartbeat-interval = 2"
    }
  }
}
`, testbed.Name, apiresource.ResourceTypeTiDBCluster)))
	ExpectNoError(err)

	r2, err := client.Create([]byte(fmt.Sprintf(`{
  "name": "downstream",
  "testbed": "%s",
  "type": "%s",
  "spec": {
    "pd": {
      "replicas": 1,
      "baseImage": "hub.pingcap.net/qa/pd",
      "version": "v5.4.0"
    },
    "tidb": {
      "replicas": 1,
      "baseImage": "hub.pingcap.net/qa/tidb",
      "version": "v5.4.0",
      "requests": {
        "cpu": 1,
        "memory": "1Gi"
      },
      "limits": {
        "cpu": 1,
        "memory": "1Gi"
      },
      "config": "[log]\nlevel = \"info\"\nnot-exist-config = 1"
    },
    "tikv": {
      "replicas": 1,
      "baseImage": "hub.pingcap.net/qa/tikv",
      "version": "v5.4.0",
      "requests": {
        "cpu": 1,
        "memory": "1Gi"
      },
      "limits": {
        "cpu": 1,
        "memory": "1Gi"
      },
      "evictLeaderTimeout": "10s",
      "config": "[storage]\nreserve-space = \"100MB\"\n[rocksdb]\nmax-open-files = 256"
    }
  }
}
`, testbed.Name, apiresource.ResourceTypeTiDBCluster)))
	ExpectNoError(err)
	return r.(*k8s.TiDBCluster), r2.(*k8s.TiDBCluster)
}

func (f *Framework) WaitForTiDBDrainerReady(drainer *k8s.TiDBDrainer, timeout time.Duration) {
	err := wait.PollImmediate(poll, timeout, func() (done bool, err error) {
		if err := drainer.Sync(); err != nil {
			return false, err
		}
		return drainer.Ready(), nil
	})
	ExpectNoError(err)
	// sleep 10 seconds waiting drainer process to start
	time.Sleep(10 * time.Second)
}

func (f *Framework) GetTiDBDrainer(handle apiresource.ResourceHandle) *k8s.TiDBDrainer {
	client := k8s.NewTiDBDrainerClient(f.KubeClientSetPack)
	r, err := client.Get(handle)
	ExpectNoError(err)
	return r.(*k8s.TiDBDrainer)
}

func (f *Framework) DeleteTiDBDrainerWaited(drainer *k8s.TiDBDrainer) {
	client := k8s.NewTiDBDrainerClient(f.KubeClientSetPack)
	ExpectNoError(client.Delete(drainer.ResourceHandle))

	err := wait.PollImmediate(poll, f.DeletionTimeout, func() (done bool, err error) {
		_, err = client.Get(drainer.ResourceHandle)
		if resource.IsResourceNotFoundError(err) {
			return true, nil
		}
		return false, nil
	})
	ExpectNoError(err)
}

func (f *Framework) ArchiveDrainer(handle apiresource.ResourceHandle, archiver func(objectName string, reader io.Reader) (*url.URL, error)) {
	client := k8s.NewTiDBDrainerClient(f.KubeClientSetPack)
	_, err := client.Artifact(handle, apiresource.GcArtifactAction{Log: true}, archiver)
	ExpectNoError(err)
}
