package workflow

import (
	"github.com/tidbcloud/chaos-svc/idl/pbgen/proto/chaos"
	"github.com/tidbcloud/chaos-svc/pkg/domain/constant"
	"github.com/tidbcloud/chaos-svc/pkg/domain/entity"
	"github.com/tidbcloud/pkgv2/bizerr"
)

const (
	entryNodeName  = "node1"
	actionNodeName = "node2"
)

// there is only one action in an experiment
type SpecConverter interface {
	Pb2Entity(action *chaos.ExperimentAction) (*entity.WorkflowSpec, bizerr.BizError)
	Entity2Pb(spec entity.WorkflowSpec) (*chaos.ExperimentAction, bizerr.BizError)
}

type specConverter struct {
}

func NewSpecConverter() SpecConverter {
	return &specConverter{}
}

func (c *specConverter) Pb2Entity(action *chaos.ExperimentAction) (*entity.WorkflowSpec, bizerr.BizError) {
	err := c.validateActionPb(action)
	if err != nil {
		return nil, err.Chain("validate")
	}

	actionNode, err := c.buildActionNodeEntity(action)
	if err != nil {
		return nil, err
	}
	spec := &entity.WorkflowSpec{
		EntryNodeName: entryNodeName,
		Templates: map[string]entity.WorkflowNodeTemplate{
			entryNodeName:  c.buildEntryNodeEntity(),
			actionNodeName: actionNode,
		},
	}
	return spec, nil
}

func (c *specConverter) Entity2Pb(spec entity.WorkflowSpec) (*chaos.ExperimentAction, bizerr.BizError) {
	actionNode, ok := spec.Templates[actionNodeName]
	if !ok {
		return nil, bizerr.New(bizerr.ErrCodeGeneralBusiness, "action node not found")
	}
	err := c.validateActionNodeEntity(actionNode)
	if err != nil {
		return nil, err.Chain("validate")
	}
	switch actionNode.Type {
	case constant.WorkflowTemplateTypePodShutdown:
		action := &chaos.ExperimentAction{
			Type: chaos.ActionKind_ACTION_KIND_POD_SHUTDOWN,
			PodShutdown: &chaos.ExperimentAction_ActionPodShutdown{
				Target: &chaos.ExperimentTarget{
					Mode:      actionNode.PodShutdown.Target.Mode,
					Component: actionNode.PodShutdown.Target.Component,
					Name:      actionNode.PodShutdown.Target.Name,
				},
			},
		}
		return action, nil
	}

	return nil, bizerr.New(bizerr.ErrCodeGeneralBusiness, "action type not supported")
}

func (c *specConverter) validateActionPb(action *chaos.ExperimentAction) bizerr.BizError {
	if action == nil {
		return bizerr.New(bizerr.ErrCodeGeneralBusiness, "empty action")
	}
	switch action.Type {
	case chaos.ActionKind_ACTION_KIND_POD_SHUTDOWN:
		if action.PodShutdown == nil {
			return bizerr.New(bizerr.ErrCodeGeneralBusiness, "empty action")
		}
		err := c.validateTargetPb(action.PodShutdown.Target)
		if err != nil {
			return err
		}
		return nil
	}

	return bizerr.New(bizerr.ErrCodeGeneralBusiness, "action type not supported")
}

func (c *specConverter) validateTargetPb(target *chaos.ExperimentTarget) bizerr.BizError {
	if target == nil {
		return bizerr.New(bizerr.ErrCodeGeneralBusiness, "empty target")
	}
	switch target.Mode {
	case chaos.TargetMode_TARGET_MODE_RANDOM:
		// do nothing
		return nil
	case chaos.TargetMode_TARGET_MODE_SPECIFIED:
		if target.Name == nil {
			return bizerr.New(bizerr.ErrCodeGeneralBusiness, "empty target name")
		}
		return nil
	}
	return bizerr.New(bizerr.ErrCodeGeneralBusiness, "target mode not supported")
}

func (c *specConverter) validateActionNodeEntity(node entity.WorkflowNodeTemplate) bizerr.BizError {
	switch node.Type {
	case constant.WorkflowTemplateTypePodShutdown:
		if node.EmbedAction == nil {
			return bizerr.New(bizerr.ErrCodeGeneralBusiness, "empty action")
		}
		if node.EmbedAction.PodShutdown == nil {
			return bizerr.New(bizerr.ErrCodeGeneralBusiness, "empty action")
		}
		return c.validateTargetPb(&node.PodShutdown.Target)
	}
	return bizerr.New(bizerr.ErrCodeGeneralBusiness, "action type not supported")
}

func (c *specConverter) buildEntryNodeEntity() entity.WorkflowNodeTemplate {
	return entity.WorkflowNodeTemplate{
		Name:     entryNodeName,
		Type:     constant.WorkflowTemplateTypeSerial,
		Children: []string{actionNodeName},
	}
}

func (c *specConverter) buildActionNodeEntity(action *chaos.ExperimentAction) (entity.WorkflowNodeTemplate, bizerr.BizError) {
	node := entity.WorkflowNodeTemplate{
		Name: actionNodeName,
	}
	switch action.Type {
	case chaos.ActionKind_ACTION_KIND_POD_SHUTDOWN:
		node.Type = constant.WorkflowTemplateTypePodShutdown
		node.EmbedAction = &entity.EmbedAction{
			PodShutdown: &entity.PodShutdownAction{
				Target: chaos.ExperimentTarget{
					Mode:      action.PodShutdown.Target.Mode,
					Component: action.PodShutdown.Target.Component,
					Name:      action.PodShutdown.Target.Name,
				},
			},
		}
		return node, nil
	}
	return entity.WorkflowNodeTemplate{}, bizerr.New(bizerr.ErrCodeGeneralBusiness, "action type not supported")
}
