package tcms

import (
	"encoding/json"
	"fmt"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/tidbcloud/pov-service/internal/model"
)

const (
	AWSUsEast1RunnerID = 60002
	AWSUsWest2RunnerID = 3
)

type ListResp[T any] struct {
	Data  []T `json:"data"`
	Total int `json:"total"`
}

type RunWorkflowParams struct {
	WorkflowID int64                 `json:"workflowID"`
	Name       string                `json:"name"`
	Source     string                `json:"source"`
	RunnerID   int64                 `json:"runnerID"`
	Params     *model.WorkloadParams `json:"workflowParams"`
}

func NewRunWorkflowParams(
	workflowID int64,
	workflowName string,
	params model.WorkloadParams,
) *RunWorkflowParams {
	now := time.Now()
	nameSuffix := now.Format("20060102_150405")
	//  I don't know whether the name need to be suffix, just follow the default name convention.
	name := fmt.Sprintf("%s_%s", workflowName, nameSuffix)

	return &RunWorkflowParams{
		WorkflowID: workflowID,
		Name:       name,
		Source:     "TCMS",
		Params:     &params,
	}
}

type WorkflowNode struct {
	Type string          `json:"type"`
	Data json.RawMessage `json:"data"`
}

type WorkflowSpec struct {
	StartNodeParams []model.WorkloadField `json:"startNodeParams"`
	Nodes           []WorkflowNode        `json:"nodes"`
}

type Workflow struct {
	ID   int64        `json:"id"`
	Name string       `json:"name"`
	Spec WorkflowSpec `json:"spec"`
	Desc string       `json:"description"`
}

// TODO: It's a temporary solution to get workload names
// it's better to get the information directly from tcms or other solid ways.
func (w *Workflow) TrafficNames() []string {
	var names []string
	re := regexp.MustCompile(`"workload_name":"([^"]*)"`)
	for _, node := range w.Spec.Nodes {
		if node.Type != "ti_workload_call" {
			continue
		}

		nodeStr := string(node.Data)
		matches := re.FindStringSubmatch(nodeStr)

		if len(matches) > 1 {
			workloadName := matches[1]
			names = append(names, workloadName)
		}
	}
	return names
}

type WorkflowTask struct {
	TaskID            string         `json:"taskID"`
	CreatedTimestamp  *time.Time     `json:"createdTimestamp"`
	FinishedTimestamp *time.Time     `json:"finishedTimestamp"`
	Status            string         `json:"status"`
	NodeName          string         `json:"nodeName"`
	Outputs           map[string]any `json:"outputs"`
	StateOutput       map[string]any `json:"stateOutput"`
	Action            struct {
		Kind string         `json:"kind"`
		Spec map[string]any `json:"spec"`
	} `json:"action"`
}

func (w WorkflowTask) HasOutput() bool {
	return len(w.StateOutput) > 0
}

func (w WorkflowTask) HasTargetStateOutput(key string) bool {
	return w.HasOutput() && w.StateOutput[key] != nil
}

type WorkflowGraph struct {
	Status string `json:"status"`

	CompletedTasks []WorkflowTask `json:"completedTasks"`
	FinalizeTasks  []WorkflowTask `json:"finalizeTasks"`
	OngoingTasks   []WorkflowTask `json:"ongoingTasks"`
}

type WorkflowExec struct {
	model.WorkloadTaskBase

	Status string `json:"status"`
	// Name is the name of workflow *execution*, not the workflow name
	Spec   WorkflowSpec  `json:"spec"`
	Graph  WorkflowGraph `json:"graph"`
	Source string        `json:"source"`
}

func (w WorkflowExec) StartedAt() *time.Time {
	for _, task := range w.Graph.CompletedTasks {
		// INIT is the dumb task which starts the workflow
		if task.NodeName == "__INIT__" {
			return task.CreatedTimestamp
		}
	}
	return nil
}

func (w WorkflowExec) Progress() float64 {
	successCount := 0
	for _, task := range w.Graph.CompletedTasks {
		// INIT is the dumb task which starts the workflow
		if task.Status == "SUCCEEDED" {
			successCount++
		}
	}
	return float64(successCount) / float64(len(w.Spec.Nodes))
}

// @deprecated
func (w WorkflowExec) Phase() string {
	if w.Status != "RUNNING" {
		return w.Status
	}

	phaseAction := map[string]string{
		"START":            "START",
		"TESTBED_CREATE":   "CREATING CLUSTER",
		"TI_WORKLOAD_CALL": "WORKLOAD",
	}

	for _, task := range w.Graph.OngoingTasks {
		if phase, ok := phaseAction[task.Action.Kind]; ok {
			if phase == "WORKLOAD" {
				if strings.Contains(task.NodeName, "_prepare") {
					return "PREPARING WORKLOAD DATA"
				} else {
					return "RUNNING WORKLOAD"
				}
			}
			return phase
		}
	}

	return "RUNNING"
}

func (w *WorkflowExec) Convert2WorkloadTask() *model.WorkloadTaskBase {
	if w == nil {
		return nil
	}

	statusCvt := map[string]model.ExecStatus{
		"RUNNING":   model.ExecStatusRunning,
		"SUCCESS":   model.ExecStatusCompleted,
		"FAILURE":   model.ExecStatusFailed,
		"FAILED":    model.ExecStatusFailed,
		"CANCELLED": model.ExecStatusStopped,
	}

	ret := model.WorkloadTaskBase{
		Typ: w.Typ,
		ExecBase: model.ExecBase{
			CreatedAt: w.CreatedAt,
			UpdatedAt: w.UpdatedAt,
			StartedAt: w.StartedAt(),
		},
		Progress:         w.Progress(),
		RelatedServiceID: strconv.Itoa(int(w.ID)),
	}

	if status, ok := statusCvt[w.Status]; ok {
		ret.Status = status
	} else {
		ret.Status = model.ExecStatusRunning
	}
	if ret.IsFinished() {
		ret.FinishedAt = w.UpdatedAt
	}

	switch ret.Status {
	case model.ExecStatusFailed:
		ret.Message = "workload client encountered an error."
	case model.ExecStatusStopped:
		ret.Message = "workload client was stopped by admin."
	}

	return &ret
}

func (w WorkflowExec) GetFinalizedTask(kind string) WorkflowTask {
	for _, task := range w.Graph.FinalizeTasks {
		if task.Action.Kind == kind {
			return task
		}
	}
	return WorkflowTask{}
}

func (w WorkflowExec) GetCompletedTask(kind string) WorkflowTask {
	for _, task := range w.Graph.CompletedTasks {
		if task.Action.Kind == kind {
			return task
		}
	}
	return WorkflowTask{}
}

func (w WorkflowExec) GetOngoingTask(kind string) WorkflowTask {
	for _, task := range w.Graph.OngoingTasks {
		if task.Action.Kind == kind {
			return task
		}
	}
	return WorkflowTask{}
}
