// Copyright (C) 2026 Yota Hamada
// SPDX-License-Identifier: GPL-3.0-or-later

package model

import (
	"errors"
	"fmt"

	"github.com/dagucloud/dagu/internal/cmn/stringutil"
	"github.com/dagucloud/dagu/internal/core"
	"github.com/dagucloud/dagu/internal/runtime"
)

// NewNodesFromSteps builds legacy Node entries with default values for each DAG step.
func NewNodesFromSteps(steps []core.Step) []*Node {
	var ret []*Node
	for _, s := range steps {
		ret = append(ret, NewPendingNode(s))
	}
	return ret
}

// NewNodesFromRuntime converts runtime node snapshots into legacy Node entries.
func NewNodesFromRuntime(nodes []runtime.NodeData) []*Node {
	var ret []*Node
	for _, node := range nodes {
		ret = append(ret, NewNodeFromRuntime(node))
	}
	return ret
}

// NewNodeFromRuntime converts a runtime node record into the legacy Node shape.
func NewNodeFromRuntime(node runtime.NodeData) *Node {
	return &Node{
		Step:       node.Step,
		Log:        node.State.Stdout,
		StartedAt:  stringutil.FormatTime(node.State.StartedAt),
		FinishedAt: stringutil.FormatTime(node.State.FinishedAt),
		Status:     node.State.Status,
		StatusText: node.State.Status.String(),
		RetriedAt:  stringutil.FormatTime(node.State.RetriedAt),
		RetryCount: node.State.RetryCount,
		DoneCount:  node.State.DoneCount,
		Error:      errText(node.State.Error),
	}
}

type Node struct {
	Step       core.Step       `json:"Step"`
	Log        string          `json:"Log"`
	StartedAt  string          `json:"StartedAt"`
	FinishedAt string          `json:"FinishedAt"`
	Status     core.NodeStatus `json:"Status"`
	RetriedAt  string          `json:"RetriedAt,omitempty"`
	RetryCount int             `json:"RetryCount,omitempty"`
	DoneCount  int             `json:"DoneCount,omitempty"`
	Error      string          `json:"Error,omitempty"`
	StatusText string          `json:"StatusText"`
}

// ToRuntimeNode converts the legacy Node representation back into a runtime node.
func (n *Node) ToRuntimeNode() *runtime.Node {
	startedAt, _ := stringutil.ParseTime(n.StartedAt)
	finishedAt, _ := stringutil.ParseTime(n.FinishedAt)
	retriedAt, _ := stringutil.ParseTime(n.RetriedAt)
	return runtime.NewNode(n.Step, runtime.NodeState{
		Status:     n.Status,
		Stdout:     n.Log,
		StartedAt:  startedAt,
		FinishedAt: finishedAt,
		RetriedAt:  retriedAt,
		RetryCount: n.RetryCount,
		DoneCount:  n.DoneCount,
		Error:      errFromText(n.Error),
	})
}

// NewPendingNode returns a legacy Node initialized in the not-started state.
func NewPendingNode(step core.Step) *Node {
	return &Node{
		Step:       step,
		StartedAt:  "-",
		FinishedAt: "-",
		Status:     core.NodeNotStarted,
		StatusText: core.NodeNotStarted.String(),
	}
}

var errNodeProcessing = errors.New("node processing error")

func errFromText(err string) error {
	if err == "" {
		return nil
	}
	return fmt.Errorf("%w: %s", errNodeProcessing, err)
}

func errText(err error) string {
	if err == nil {
		return ""
	}
	return err.Error()
}
