package devops

import (
	"fmt"
	"sort"
	"strings"
)

// ApprovalNodeSummary describes one node of an approval chain in a form that
// is useful to both an agent and a human reading the tool output.
type ApprovalNodeSummary struct {
	Name      string   `json:"name,omitempty"`
	Method    string   `json:"method,omitempty"`
	Approvers []string `json:"approvers"`
	Condition string   `json:"condition,omitempty"`
}

// Summarize flattens the approval template into a readable chain. An empty
// result means the operation is approved as soon as it is created.
func (t ApprovalTemplate) Summarize() []ApprovalNodeSummary {
	out := make([]ApprovalNodeSummary, 0, len(t.Nodes))
	for _, node := range t.Nodes {
		if node == nil {
			continue
		}
		for _, effective := range effectiveNodes(node) {
			out = append(out, ApprovalNodeSummary{
				Name:      effective.Name,
				Method:    effective.Method,
				Approvers: describeApprover(&effective.Approver),
				Condition: effective.Condition,
			})
		}
	}
	return out
}

func effectiveNodes(node *ApproverNode) []*ApproverNode {
	if len(node.Parallel) == 0 {
		return []*ApproverNode{node}
	}
	out := make([]*ApproverNode, 0, len(node.Parallel))
	for _, child := range node.Parallel {
		if child != nil {
			out = append(out, child)
		}
	}
	return out
}

func describeApprover(a *Approver) []string {
	if a == nil {
		return nil
	}

	out := make([]string, 0, 4)
	if a.Email != "" {
		out = append(out, a.Email)
	}
	out = append(out, a.Emails...)
	if a.UserGroup != "" {
		out = append(out, "group:"+a.UserGroup)
	}
	for _, group := range a.UserGroups {
		out = append(out, "group:"+group)
	}
	if a.ApplicantLeader {
		out = append(out, "applicant leader")
	}
	if a.SelfApprove {
		out = append(out, "applicant (self-approve)")
	}
	if a.FromParam != nil {
		out = append(out, describeFromParam(a.FromParam)...)
	}
	return out
}

func describeFromParam(f *ApproverFromParam) []string {
	out := make([]string, 0, len(f.ParamValueMapping)+2)
	if f.UseParamValue {
		out = append(out, fmt.Sprintf("value of param %s", f.ParamName))
	}

	values := make([]string, 0, len(f.ParamValueMapping))
	for value := range f.ParamValueMapping {
		values = append(values, value)
	}
	sort.Strings(values)
	for _, value := range values {
		nested := describeApprover(f.ParamValueMapping[value])
		if len(nested) == 0 {
			continue
		}
		out = append(out, fmt.Sprintf("%s=%s -> %s", f.ParamName, value, strings.Join(nested, " / ")))
	}

	if nested := describeApprover(f.DefaultApprover); len(nested) > 0 {
		out = append(out, "default -> "+strings.Join(nested, " / "))
	}
	return out
}
