package migration

import (
	"fmt"
	"io"
	"path/filepath"
	"strconv"

	"github.com/tidbcloud/observability/vm-migration/internal/printer"
)

// RenderText writes s as a human-readable table to w.
//
// Consumed by the HTTP summary endpoint.
func RenderText(w io.Writer, s *Summary) {
	rows := make([]loopSummaryRow, 0, len(s.Rows))
	for _, r := range s.Rows {
		rows = append(rows, loopSummaryRow{
			tenant:   r.Tenant,
			step:     r.Step,
			status:   r.Status,
			attempt:  r.Attempt,
			dur:      formatLoopDuration(r.Duration),
			shards:   formatCountStr(r.Imported, r.ShardsTotal),
			verified: formatCountStr(r.Verified, r.ShardsTotal),
			migrated: formatBytesOrDash(r.BytesMigrated),
			speed:    formatRateOrDash(r.MigrateRate),
		})
	}

	printer.Printf(w, "%s  updatedAt=%s", filepath.Base(s.StatusPath), orDash(s.UpdatedAt))
	printer.Printf(w, "total=%d  done=%d  running=%d  failed=%d  pending=%d  complete=%t  (%d%% complete)\n",
		s.Total, s.Done, s.Running, s.Failed, s.Pending, s.Complete, s.Percent)

	cols := []printer.ColumnSpec{
		{Name: "#", Align: ">"},
		{Name: "tenant", Align: "<"},
		{Name: "step", Align: "<"},
		{Name: "status", Align: "<"},
		{Name: "attempt", Align: ">"},
		{Name: "dur", Align: ">"},
		{Name: "shards", Align: ">"},
		{Name: "verified", Align: ">"},
		{Name: "migrated", Align: ">"},
		{Name: "speed", Align: ">"},
	}
	widths := computeLoopSummaryWidths(rows, s.Total)
	printer.PrintRow(w, cols, widths)
	printer.PrintSeparator(w, widths)
	for i, r := range rows {
		values := []any{
			i + 1, r.tenant, r.step, r.status, r.attempt, r.dur,
			r.shards, r.verified, r.migrated, r.speed,
		}
		printer.PrintValues(w, values, cols, widths)
	}
	printer.Printf(w, "\ntotal time: %s   total migrated: %s",
		formatLoopDuration(s.TotalDuration), formatBytesOrDash(s.TotalMigrated))
}

func orDash(s string) string {
	if s == "" {
		return "-"
	}
	return s
}

func formatCountStr(done, total int) string {
	if total == 0 {
		return "-"
	}
	suffix := "no"
	if done == total {
		suffix = "yes"
	}
	return fmt.Sprintf("%d/%d (%s)", done, total, suffix)
}

func formatLoopDuration(seconds float64) string {
	if seconds <= 0 {
		return "-"
	}
	if seconds < 60 {
		return fmt.Sprintf("%ds", int(seconds))
	}
	if seconds < 3600 {
		m := int(seconds) / 60
		s := int(seconds) % 60
		if s == 0 {
			return fmt.Sprintf("%dm", m)
		}
		return fmt.Sprintf("%dm%ds", m, s)
	}
	if seconds < 86400 {
		h := int(seconds) / 3600
		m := (int(seconds) % 3600) / 60
		if m == 0 {
			return fmt.Sprintf("%dh", h)
		}
		return fmt.Sprintf("%dh%dm", h, m)
	}
	d := int(seconds) / 86400
	rem := int(seconds) - d*86400
	h := rem / 3600
	m := (rem % 3600) / 60
	out := fmt.Sprintf("%dd", d)
	if h > 0 {
		out += fmt.Sprintf("%dh", h)
	}
	if m > 0 {
		out += fmt.Sprintf("%dm", m)
	}
	return out
}

func formatBytesOrDash(bytes int64) string {
	if bytes <= 0 {
		return "-"
	}
	return printer.FormatBytes(float64(bytes))
}

func formatRateOrDash(rate float64) string {
	if rate <= 0 {
		return "-"
	}
	return printer.FormatBytes(rate) + "/s"
}

// loopSummaryRow is one entry in the printed table.
type loopSummaryRow struct {
	tenant, step, status string
	attempt              int
	dur                  string
	shards, verified     string
	migrated, speed      string
}

// computeLoopSummaryWidths returns the per-column widths for the summary table.
//
// Column header labels must stay in sync with the cols literal in RenderText.
func computeLoopSummaryWidths(rows []loopSummaryRow, total int) []int {
	maxN := len(strconv.Itoa(total))
	if maxN < 1 {
		maxN = 1
	}
	widths := []int{
		maxN,
		len("tenant"),
		len("step"),
		len("status"),
		len("attempt"),
		len("dur"),
		len("shards"),
		len("verified"),
		len("migrated"),
		len("speed"),
	}
	fields := func(r loopSummaryRow) []string {
		return []string{"", r.tenant, r.step, r.status, "", r.dur, r.shards, r.verified, r.migrated, r.speed}
	}
	for _, r := range rows {
		for i, s := range fields(r) {
			if i >= len(widths) {
				break
			}
			if len(s) > widths[i] {
				widths[i] = len(s)
			}
		}
	}
	return widths
}
