package gc

import (
	"fmt"
	"strconv"
	"time"

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

	apiresource "github.com/pingcap/test-infra/common/model/resource"
	"github.com/pingcap/test-infra/sdk/resource"
	"github.com/pingcap/test-infra/services/rms/service/provider"
)

var (
	_ provider.ReserverInterface = &DefaultReserver{}
)

type DefaultReserver struct {
	Clients func(resourceType apiresource.ResourceType, testbed string) (resource.Client, error)
}

func NewDefaultReserver(clients func(resourceType apiresource.ResourceType, testbed string) (resource.Client, error)) *DefaultReserver {
	return &DefaultReserver{Clients: clients}
}

func (d *DefaultReserver) Reserve(handle apiresource.ResourceHandle, action apiresource.GcReserveEnvAction) error {
	client, err := d.Clients(handle.Type, handle.Testbed)
	if err != nil {
		return err
	}
	return client.ReserveEnv(handle, action)
}

func parseUnixTimestamp(ts string) (*time.Time, error) {
	i, err := strconv.ParseInt(ts, 10, 64)
	if err != nil {
		return nil, err
	}
	tm := time.Unix(i, 0)
	return &tm, nil
}

var (
	ErrNoLeaseInfoFound = fmt.Errorf("no renewal and lease info found")
)

func GetLeaseInfoOn(ns *corev1.Namespace) (expireTs *time.Time, lease *time.Duration, err error) {
	expireTs, lease = new(time.Time), new(time.Duration)
	if ns.Annotations == nil {
		return nil, nil, ErrNoLeaseInfoFound
	}
	l, e := ns.Annotations["lease"]
	if !e {
		return nil, nil, ErrNoLeaseInfoFound
	}
	*lease, err = time.ParseDuration(l)
	if err != nil {
		return nil, nil, err
	}
	ets, e := ns.Annotations["expireTs"]
	if e {
		expireTs, err = parseUnixTimestamp(ets)
		if err != nil {
			return nil, nil, err
		}
		return expireTs, lease, nil
	}
	// fallback uses lastRenewalTs + lease to calc expireTs
	renewalTs, e := ns.Annotations["lastRenewalTs"]
	if !e {
		return nil, nil, ErrNoLeaseInfoFound
	}
	lastRenewalTs, err := parseUnixTimestamp(renewalTs)
	if err != nil {
		return nil, nil, err
	}
	*expireTs = lastRenewalTs.Add(*lease)
	return expireTs, lease, nil
}

func SetLeaseInfoOn(ns *corev1.Namespace, renewalTime time.Time, lease string) error {
	lastRenewalTs := fmt.Sprintf("%d", renewalTime.Unix())
	if ns.Annotations == nil {
		ns.Annotations = map[string]string{}

	}
	if lease == "" {
		lease = ns.Annotations["lease"]
	}
	if lease == "" {
		return fmt.Errorf("the lease is empty")
	}

	leaseDur, err := time.ParseDuration(lease)
	if err != nil {
		return pkgErrors.Wrapf(err, "parse lease `%s` error", lease)
	}
	expireTs := fmt.Sprintf("%d", renewalTime.Add(leaseDur).Unix())

	ns.Annotations["lastRenewalTs"] = lastRenewalTs
	ns.Annotations["expireTs"] = expireTs
	ns.Annotations["lease"] = lease
	return nil
}

func SetExpireTsOn(ns *corev1.Namespace, expireTs time.Time, lease string) error {
	if lease == "" {
		return fmt.Errorf("the lease is empty")
	}
	if ns.Annotations == nil {
		ns.Annotations = map[string]string{}

	}
	_, err := time.ParseDuration(lease)
	if err != nil {
		return pkgErrors.Wrapf(err, "parse lease `%s` error", lease)
	}
	ns.Annotations["lastRenewalTs"] = fmt.Sprintf("%d", time.Now().Unix())
	ns.Annotations["expireTs"] = fmt.Sprintf("%d", expireTs.Unix())
	ns.Annotations["lease"] = lease
	return nil
}
