package model

import (
	"encoding/json"
	"slices"
	"time"

	"github.com/tidbcloud/pov-service/internal/pkg/utils"
	"github.com/tidbcloud/pov-service/internal/pkg/uuid"
)

type ScriptPhaseExec interface {
	GetName() string
	GetType() ScriptPhaseType
	GetStatus() ExecStatus
	SetStatus(ExecStatus)
	// GetMessage() string
	GetStartDuration() time.Duration
	GetDetailJSON() (json.RawMessage, error)
}

type ScriptPhaseExecBase struct {
	Name          string          `json:"name"`
	Type          ScriptPhaseType `json:"type"`
	Status        ExecStatus      `json:"state"`
	Message       string          `json:"message"`
	StartDuration time.Duration   `json:"startDuration" swaggertype:"integer" format:"int64"`
}

func (s *ScriptPhaseExecBase) GetName() string {
	return s.Name
}

func (s *ScriptPhaseExecBase) GetType() ScriptPhaseType {
	return s.Type
}

func (s *ScriptPhaseExecBase) GetStatus() ExecStatus {
	return s.Status
}

func (s *ScriptPhaseExecBase) SetStatus(status ExecStatus) {
	s.Status = status
}

func (s *ScriptPhaseExecBase) GetMessage() string {
	return s.Message
}

func (s *ScriptPhaseExecBase) GetStartDuration() time.Duration {
	return s.StartDuration
}

func (s *ScriptPhaseExecBase) GetDetailJSON() (json.RawMessage, error) {
	return nil, nil
}

type ScriptMutationPhaseExec struct {
	ScriptPhaseExecBase
	Mutations []ScriptMutation `json:"mutations"`
}

func (s *ScriptMutationPhaseExec) GetDetailJSON() (json.RawMessage, error) {
	return json.Marshal(s.Mutations)
}

type ScriptProceedPhaseExec struct {
	ScriptPhaseExecBase
}

type ScriptExec struct {
	ID            string        `json:"id"`
	TestID        string        `json:"testId"`
	ScriptID      int64         `json:"scriptId"`
	Name          string        `json:"name"`
	Description   string        `json:"description,omitempty"`
	CreatorEmail  string        `json:"creatorEmail,omitempty"`
	TotalDuration time.Duration `json:"totalDuration" swaggertype:"integer" format:"int64"`
	ExecBase

	Phases []ScriptPhaseExec `json:"phases"`

	// [optional]
	WorkloadTest *WorkloadTest `json:"test"`
}

func NewScriptExec(
	testID string,
	script Script,
) ScriptExec {
	id := uuid.NewString()
	phases := make([]ScriptPhaseExec, len(script.Phases))
	for i, phase := range script.Phases {
		phases[i] = parsePhase2PhaseExec(phase)
	}

	return ScriptExec{
		ID:            id,
		ScriptID:      script.ID,
		TestID:        testID,
		Name:          script.Name,
		Description:   script.Description,
		CreatorEmail:  script.CreatorEmail,
		TotalDuration: script.TotalDuration,
		Phases:        phases,
		ExecBase: ExecBase{
			Status:    ExecStatusRunning,
			StartedAt: utils.TimePointer(time.Now().UTC()),
		},
	}
}

func (s *ScriptExec) OnStop() {
	for i := range s.Phases {
		status := s.Phases[i].GetStatus()
		if slices.Contains(ActiveStatuses, status) {
			s.Phases[i].SetStatus(ExecStatusStopped)
		}
	}

	s.Status = ExecStatusStopped

	now := time.Now().UTC()
	s.UpdatedAt = utils.TimePointer(now)
	s.FinishedAt = utils.TimePointer(now)
}

func (s *ScriptExec) OnFailure() {
	for i := range s.Phases {
		status := s.Phases[i].GetStatus()
		if slices.Contains(ActiveStatuses, status) {
			s.Phases[i].SetStatus(ExecStatusStopped)
		}
	}

	s.Status = ExecStatusFailed

	now := time.Now().UTC()
	s.UpdatedAt = utils.TimePointer(now)
	s.FinishedAt = utils.TimePointer(now)
}

func (s *ScriptExec) OnComplete() {
	for i := range s.Phases {
		status := s.Phases[i].GetStatus()
		if slices.Contains(ActiveStatuses, status) {
			s.Phases[i].SetStatus(ExecStatusStopped)
		}
	}

	s.Status = ExecStatusCompleted
	now := time.Now().UTC()
	s.UpdatedAt = utils.TimePointer(now)
	s.FinishedAt = utils.TimePointer(now)
}
