package local

import (
	"context"
	"database/sql"
	"encoding/json"
	"errors"
	"net/url"
	"os"

	prometheus "github.com/prometheus/client_golang/api"
	prometheus_api "github.com/prometheus/client_golang/api/prometheus/v1"

	apiresource "github.com/pingcap/test-infra/common/model/resource"
	"github.com/pingcap/test-infra/sdk/core"
	"github.com/pingcap/test-infra/sdk/pkg/util/pdutil"
	"github.com/pingcap/test-infra/sdk/resource"
)

var _ resource.TiDBCluster = &TiDBCluster{}

type TiDBCluster struct {
	// api object
	apiresource.TiDBCluster
}

func (t TiDBCluster) FQDN() string {
	return resource.FQDN(t)
}

func (t *TiDBCluster) Handle() apiresource.ResourceHandle {
	return t.TiDBCluster.ResourceHandle
}

func (t *TiDBCluster) ServiceURL(typ resource.TiDBAddrType) (*url.URL, error) {
	urls := []string{}
	switch typ {
	case resource.DBAddr, resource.DBStatus:
		if typ == resource.DBStatus {
			return nil, errors.New("local DBStatus unavailable")
		}

		dburls, ok := os.LookupEnv("TI_PARAM_RES_" + t.ResourceHandle.Name + "_TIDB_URL")
		if !ok {
			return nil, errors.New("local tidbaddr not found")
		}
		if err := json.Unmarshal([]byte(dburls), &urls); err != nil {
			return nil, err
		}
		return url.Parse(urls[0])

	case resource.KVAddr, resource.KVStatus:
		if typ == resource.DBStatus {
			return nil, errors.New("local KVStatus unavailable")
		}

		kvurls, ok := os.LookupEnv("TI_PARAM_RES_" + t.ResourceHandle.Name + "_TIKV_URL")
		if !ok {
			return nil, errors.New("local tikvaddr not found")
		}
		if err := json.Unmarshal([]byte(kvurls), &urls); err != nil {
			return nil, err
		}
		return url.Parse(urls[0])

	case resource.PDAddr:
		pdurls, ok := os.LookupEnv("TI_PARAM_RES_" + t.ResourceHandle.Name + "_PD_URL")
		if !ok {
			return nil, errors.New("local pdaddr not found")
		}
		if err := json.Unmarshal([]byte(pdurls), &urls); err != nil {
			return nil, err
		}
		return url.Parse(urls[0])

	case resource.TiFlashAddr:
		tiflashurls, ok := os.LookupEnv("TI_PARAM_RES_" + t.ResourceHandle.Name + "_TIFLASH_URL")
		if !ok {
			return nil, errors.New("local tiflashaddr not found")
		}
		if err := json.Unmarshal([]byte(tiflashurls), &urls); err != nil {
			return nil, err
		}
		return url.Parse(urls[0])

	case resource.Prometheus:
		pmurls, ok := os.LookupEnv("TI_PARAM_RES_" + t.ResourceHandle.Name + "_PROMETHEUS_URL")
		if !ok {
			return nil, errors.New("local prometheus addr not found")
		}
		if err := json.Unmarshal([]byte(pmurls), &urls); err != nil {
			return nil, err
		}
		return url.Parse(urls[0])

	default:
		return nil, errors.New("service is unavailable")
	}
}

func (t *TiDBCluster) PeerURLs(typ resource.TiDBAddrType) ([]*url.URL, error) {
	panic("unavailable api")
}

func (t *TiDBCluster) GetDB(dbName string) (db *sql.DB, err error) {
	addr, err := t.ServiceURL(resource.DBAddr)
	if err != nil {
		return nil, err
	}

	db, err = sql.Open("mysql", resource.BuildDSN(addr, dbName))
	if err != nil {
		return nil, err
	}
	if err := db.Ping(); err != nil {
		return nil, err
	}
	return db, nil
}

func (t *TiDBCluster) Exec(opts resource.TiDBClusterExecOptions) (stdout string, stderr string, exitCode int, err error) {
	panic("unavailable api")
}

func (t *TiDBCluster) UpdateConfig(opts resource.TiDBClusterUpdateConfigOptions) error {
	panic("unavailable api")
}

func (t TiDBCluster) UpdateConfigs(opts resource.TiDBClusterUpdateConfigsOptions) error {
	panic("unavailable api")
}

func (t *TiDBCluster) Upgrade(opts resource.TiDBClusterUpgradeOptions) error {
	panic("unavailable api")
}

func (t *TiDBCluster) Scale(opts resource.TiDBClusterScaleOptions) error {
	panic("unavailable api")
}

func (t *TiDBCluster) SearchLog(ctx context.Context, opts resource.TiDBClusterSearchLogOptions) ([]string, error) {
	panic("unavailable api")
}

func (t *TiDBCluster) GetSpec(comp resource.TiDBCompType) (*apiresource.TiDBClusterComponentSpec, error) {
	panic("unavailable api")
}

func (t *TiDBCluster) GetStatus(comp resource.TiDBCompType) (*apiresource.TiDBClusterComponentStatus, error) {
	panic("unavailable api")
}

func (t *TiDBCluster) WatchOOM(ctx context.Context, opts resource.TiDBClusterOOMWatchOptions) (<-chan resource.OOMEvent, error) {
	panic("unavailable api")
}

func (t *TiDBCluster) ChaosSelect(selector resource.TiDBClusterSelectorOpts) ([]resource.ChaosSelector, error) {
	panic("unavailable api")
}

func (t *TiDBCluster) ChaosClient() resource.ChaosClient {
	panic("unavailable api")
}

func (t *TiDBCluster) PrometheusClient() (prometheus.Client, error) {
	panic("unavailable api")
}

func (t *TiDBCluster) PrometheusAPI() (prometheus_api.API, error) {
	panic("unavailable api")
}

func (t *TiDBCluster) PDClient() (*pdutil.Client, error) {
	panic("unavailable api")
}

func (t *TiDBCluster) GetNodes(resource.TiDBCompType) ([]resource.TiDBComponentNode, error) {
	panic("unavailable api")
}

type tidbGetter struct{ handle apiresource.ResourceHandle }

func (t *tidbGetter) Get() (core.Resource, error) {
	return &TiDBCluster{
		TiDBCluster: apiresource.TiDBCluster{
			ResourceHandle: t.handle,
		},
	}, nil
}

func init() {
	core.RegisterProvider(resource.FQDN(TiDBCluster{}),
		func(handle apiresource.ResourceHandle) core.ResourceGetter { return &tidbGetter{handle: handle} })
}
