// Package worker_sampledataapp add by blacknc <blacknc@163.com> at 2023/09/22 17:47:28
package worker_sampledataapp

import (
	"context"
	"dataservices/commons/errs"
	"dataservices/commons/utils"
	"dataservices/internal/entity"
	"dataservices/internal/repository"
	"encoding/json"
	"time"

	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
)

func SampleDataappTaskHandler(ctx context.Context, ct *entity.CommonTask) error {
	startTime := time.Now()
	tilog.Info(ctx, "sample dataapp task start...", zap.Uint64("task_id", ct.ID), zap.Uint64("dataapp_id", ct.EventID))
	defer func() {
		tilog.Info(ctx, "sample dataapp task completed", zap.Uint64("task_id", ct.ID), zap.Uint64("dataapp_id", ct.EventID), zap.Duration("cost_time", time.Since(startTime)))
	}()

	var sampleDataappCtx SampleDataappContext
	sampleDataappCtx.ctx = ctx
	sampleDataappCtx.task = ct

	var taskContent entity.SampleDataTaskContent
	if err := ct.UnmarshalContent(&taskContent); err != nil {
		return errs.NewError(errs.ErrProhibitRetry, "Parse data app task content failed", nil)
	}
	sampleDataappCtx.taskContent = &taskContent

	dataapp, err := repository.GetDataApp(ct.EventID)
	if err != nil {
		return err
	}
	sampleDataappCtx.dataapp = dataapp

	dataappID := dataapp.ID
	appClusters, err := repository.ListAppClusterByAppId(ctx, dataappID)
	if err != nil {
		return err
	} else if len(appClusters) == 0 || appClusters[0].ClusterID == 0 {
		return errs.NewError(errs.ErrProhibitRetry, "Dataapp needs to bind at least one datasource", nil)
	}
	sampleDataappCtx.clusterID = appClusters[0].ClusterID

	for i := len(taskContent.States) - 1; i >= 0; i-- {
		switch taskContent.States[i].State {
		case entity.SampleDataTaskStateSuccee:
			sampleDataappCtx.state = &SuccessState{}
		case entity.SampleDataTaskStateImportData:
			sampleDataappCtx.state = &ImportDataState{}
		case entity.SampleDataTaskStateCreateEndpoint:
			sampleDataappCtx.state = &CreateEndpointState{}
		case entity.SampleDataTaskStateDeployEndpoint:
			sampleDataappCtx.state = &DeployEndpointState{}
		}
		if sampleDataappCtx.state != nil {
			break
		}
	}
	if sampleDataappCtx.state == nil {
		sampleDataappCtx.state = &ImportDataState{}
	}

	return sampleDataappCtx.Exec()
}

type SampleDataappContext struct {
	ctx         context.Context
	task        *entity.CommonTask
	taskContent *entity.SampleDataTaskContent
	dataapp     *entity.DataApp
	clusterID   uint64
	state       SampleDataappState
	err         error
}

func (c *SampleDataappContext) Exec() error {
	c.Trans(c.state)
	return c.err
}

func (c *SampleDataappContext) Trans(s SampleDataappState) {
	tilog.Info(c.ctx, "sample dataapp task trans to new state", zap.Uint64("task_id", c.task.ID), zap.Uint64("dataapp_id", c.dataapp.ID), zap.String("to_state", s.String()))

	c.taskContent.States = append(c.taskContent.States, entity.SampleDataTaskRecord{
		State:     s.String(),
		StartTime: utils.Now(),
	})
	content, _ := json.Marshal(c.taskContent)
	c.task.Content = string(content)
	if err := repository.UpdateCommonTask(c.ctx, c.task); err != nil {
		c.err = err
		return
	}

	c.state = s
	c.state.Exec(c)
}

type SampleDataappState interface {
	String() string
	Exec(c *SampleDataappContext)
}
