package compose

import (
	"context"
	"fmt"
	"strconv"
	"strings"

	"charm.land/lipgloss/v2"
	"github.com/psviderski/uncloud/internal/cli/tui"
	"github.com/psviderski/uncloud/pkg/api"
	"github.com/psviderski/uncloud/pkg/client/deploy"
	"github.com/psviderski/uncloud/pkg/client/deploy/operation"
)

// Plan holds the compose-level deployment plan with volume and service operations.
type Plan struct {
	Volumes  []*operation.CreateVolumeOperation
	Services []*deploy.ServicePlan
}

// IsEmpty returns true if the plan has no volume or service operations.
func (p *Plan) IsEmpty() bool {
	return len(p.Volumes) == 0 && len(p.Services) == 0
}

// Format renders the entire deployment plan as a styled tree with a summary footer.
func (p *Plan) Format() string {
	var out strings.Builder

	// Format volume operations.
	for _, op := range p.Volumes {
		out.WriteString(op.Format())
		out.WriteString("\n")
	}
	if len(p.Volumes) > 0 {
		out.WriteString("\n")
	}

	// Format service plans.
	for _, svcPlan := range p.Services {
		out.WriteString(svcPlan.Format())
		out.WriteString("\n")
	}

	// Format summary footer.
	summary := p.formatSummary()
	out.WriteString(tui.Faint.Render(strings.Repeat("─", lipgloss.Width(summary))))
	out.WriteString("\n")
	out.WriteString(summary)
	out.WriteString("\n")

	return out.String()
}

// formatSummary counts all operations across the plan and renders the summary footer.
func (p *Plan) formatSummary() string {
	var createCount, startFirstCount, stopFirstCount, removeCount int
	machines := make(map[string]struct{})

	for _, op := range p.Volumes {
		machines[op.MachineID] = struct{}{}
		createCount++
	}

	for _, svcPlan := range p.Services {
		for _, op := range svcPlan.Operations {
			switch o := op.(type) {
			case *operation.RunContainerOperation:
				machines[o.MachineID] = struct{}{}
				createCount++
			case *operation.ReplaceContainerOperation:
				machines[o.MachineID] = struct{}{}
				if o.Order == api.UpdateOrderStopFirst {
					stopFirstCount++
				} else {
					startFirstCount++
				}
			case *operation.RemoveContainerOperation:
				machines[o.MachineID] = struct{}{}
				removeCount++
			case *operation.StopContainerOperation:
				machines[o.MachineID] = struct{}{}
				removeCount++
			}
		}
	}

	var parts []string
	if createCount > 0 {
		parts = append(parts,
			tui.BoldGreen.Render(strconv.Itoa(createCount))+" "+tui.Green.Render("create"))
	}
	if startFirstCount > 0 {
		parts = append(parts,
			tui.BoldGreen.Render(strconv.Itoa(startFirstCount))+" "+tui.Green.Render("replace (start-first)"))
	}
	if stopFirstCount > 0 {
		parts = append(parts,
			tui.BoldYellow.Render(strconv.Itoa(stopFirstCount))+" "+tui.Yellow.Render("replace (stop-first)"))
	}
	if removeCount > 0 {
		parts = append(parts,
			tui.BoldRed.Render(strconv.Itoa(removeCount))+" "+tui.Red.Render("remove"))
	}

	machinesWord := "machines"
	if len(machines) == 1 {
		machinesWord = "machine"
	}
	parts = append(parts, fmt.Sprintf("across %s %s", tui.Bold.Render(strconv.Itoa(len(machines))), machinesWord))

	sep := " " + tui.Faint.Render("·") + " "
	return strings.Join(parts, sep)
}

// Execute runs all volume operations followed by all service operations.
func (p *Plan) Execute(ctx context.Context, cli operation.Client) error {
	for _, op := range p.Volumes {
		if err := op.Execute(ctx, cli); err != nil {
			return err
		}
	}
	for _, sp := range p.Services {
		if err := sp.Execute(ctx, cli); err != nil {
			return err
		}
	}
	return nil
}
