package tepserver

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/pingcap/log"
	"github.com/pingcap/tep/pkg/apis/apiserver/v1alpha1"
	"github.com/pingcap/tep/pkg/operator"
	"github.com/pingcap/tep/pkg/utils"
	operatorv1alpha1 "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1"
	"go.uber.org/zap"
)

const (
	DefaultSchedule        = "Tuesday,Saturday 0:30"
	DefaultMaxReservedTime = "168h"
)

func (s *Server) CreateInstance(admin string, instance *v1alpha1.TiDBCluster) (*v1alpha1.TiDBCluster, utils.APIStatus) {
	if strings.TrimSpace(instance.PD.StorageClassName) == "" {
		instance.PD.StorageClassName = s.PDStorageClass
	}
	if strings.TrimSpace(instance.TiKV.StorageClassName) == "" {
		instance.TiKV.StorageClassName = s.TiKVStorageClass
	}
	if strings.TrimSpace(instance.Monitor.StorageClassName) == "" {
		instance.Monitor.StorageClassName = s.MonitorStorageClass
	}

	c := s.newAPIServerClient("/tidbclusters")
	instanceBytes, err := json.Marshal(instance)
	if err != nil {
		return nil, utils.NewAPIStatus(http.StatusInternalServerError,
			fmt.Sprintf("unable to encode request %v", instance))
	}
	resp, err := c.Post(bytes.NewBuffer(instanceBytes))
	if err != nil {
		return nil, utils.NewAPIStatus(utils.ParseHTTPError(err),
			fmt.Sprintf("error creating tidbcluster, %v", err.Error()))
	}
	defer utils.CallDeferMethod(resp.Body.Close, "failed to close response body")

	tidbCluster, err := parseTiDBClusterResponse(resp)
	if err != nil {
		return nil, utils.NewAPIStatus(http.StatusInternalServerError, err.Error())
	}

	status := utils.NewAPIStatus(resp.StatusCode, resp.Status)
	var instanceID string
	if status.OK() {
		instanceID = tidbCluster.GetName()
		if strings.TrimSpace(instanceID) == "" {
			return nil, utils.NewAPIStatus(http.StatusInternalServerError,
				fmt.Sprintf("TiDBCluster Name is empty: %v", tidbCluster))
		}
	} else {
		return tidbCluster, status
	}

	scheduleBackup := &v1alpha1.TiDBClusterScheduledBackup{
		MaxReservedTime: DefaultMaxReservedTime,
		Schedule:        DefaultSchedule,
		TiDBClusterBackupTemplate: v1alpha1.TiDBClusterBackupTemplate{
			Cluster: instanceID,
			Type:    string(operatorv1alpha1.BackupTypeFull),
			Method:  operator.BackupMethodLogic,
		},
	}
	_, scheduleBackupStatus := s.CreateScheduledBackup(instanceID, scheduleBackup)
	if !scheduleBackupStatus.OK() {
		return nil, scheduleBackupStatus
	}

	return tidbCluster, status
}

func parseTiDBClusterResponse(resp *http.Response) (*v1alpha1.TiDBCluster, error) {
	var tidbCluster v1alpha1.TiDBCluster
	tidbClusterBytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("unable to read response %v", resp)
	}
	err = json.Unmarshal(tidbClusterBytes, &tidbCluster)
	if err != nil {
		log.Error("unable to decode response", zap.Any("data", resp))
		return nil, fmt.Errorf("unable to decode response %v", resp)
	}
	return &tidbCluster, nil
}
