package k8s

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

	"github.com/pingcap/errors"
	corev1 "k8s.io/api/core/v1"

	"github.com/pingcap/test-infra/sdk/core"

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

var _ resource.MySQL = &MySQLNode{}

type MySQLNode struct {
	WorkloadNode
	// reader service can be used to debug mysql node
	ReaderService *corev1.Service
}

func (t *MySQLNode) SinkEndpoint() (*url.URL, error) {
	return t.ServiceURL(resource.DBAddr)
}

// ServiceURL can be used to get mysql endpoint
func (t *MySQLNode) ServiceURL(typ resource.TiDBAddrType) (*url.URL, error) {
	if typ != resource.DBAddr {
		return nil, errors.New(fmt.Sprintf("invalid TiDBAddressType: %+v", typ))
	}
	// svc-name.ns-name
	return url.Parse(fmt.Sprintf("mysql://root:@%s.%s:3306", t.Name(), t.Namespace()))
}

func (t *MySQLNode) PeerURLs(typ resource.TiDBAddrType) ([]*url.URL, error) {
	if typ != resource.DBAddr {
		return nil, errors.New(fmt.Sprintf("invalid TiDBAddressType: %+v", typ))
	}

	var (
		format   = "mysql://root:@%s-%d.%s-peer.%s:3306"
		replicas = 1
		urls     = make([]*url.URL, replicas)
		err      error
	)

	for i := 0; i < replicas; i++ {
		urls[i], err = url.Parse(fmt.Sprintf(format, t.Name(), i, t.Name(), t.Namespace()))
		if err != nil {
			return nil, err
		}
	}
	return urls, nil
}

func (t *MySQLNode) Ready() bool {
	if !t.WorkloadNode.Ready() {
		return false
	}
	if config.MySQLCluster.ReadyCheck.PingDB {
		if err := t.Ping(); err != nil {
			return false
		}
	}
	return true
}

func (t *MySQLNode) Name() string {
	return t.WorkloadNode.ResourceHandle.Name
}

func (t *MySQLNode) Namespace() string {
	return t.WorkloadNode.KubeClientSet.Namespace
}

// Ping the mysql node, to check health
func (t *MySQLNode) Ping() error {
	if err := pingDB(t); err != nil {
		return errors.Annotatef(err, "Mysql ping failed")
	}
	return nil
}

func (t *MySQLNode) GetDB(dbName string) (db *sql.DB, err error) {
	return getDB(t, dbName)
}

type mysqlGetter struct{ handle apiresource.ResourceHandle }

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

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