package ml

import (
	"regexp"
	"strconv"
)

// SdcliProgress contiene el estado de progreso parseado de una linea de stderr de sd-cli.
type SdcliProgress struct {
	Step       int     `json:"step"`
	TotalSteps int     `json:"total_steps"`
	ItPerSec   float64 `json:"it_per_sec"`
	Percent    float64 `json:"percent"`
}

// reProgress1 parsea el formato compacto: "  3/30 |  0.84it/s |  10%"
var reProgress1 = regexp.MustCompile(`\s*(\d+)\s*/\s*(\d+)\s*\|[^|]*?([\d.]+)\s*it/s[^|]*?\|\s*([\d.]+)\s*%`)

// reProgress2 parsea el formato verbose: "sampling: step 3 of 30 (0.84 it/s)"
var reProgress2 = regexp.MustCompile(`step\s+(\d+)\s+of\s+(\d+)\s*\(\s*([\d.]+)\s*it/s\)`)

// reProgress3 parsea el formato minimal: "step 3/30" o "progress: 3/30"
var reProgress3 = regexp.MustCompile(`(?:progress[:\s]+)?(\d+)\s*/\s*(\d+)`)

// SdcliParseProgress parsea una linea de stderr de stable-diffusion.cpp / sd-cli
// y extrae el estado de progreso. Retorna (SdcliProgress, true) si la linea
// contiene informacion de progreso reconocible; (zero, false) en caso contrario.
// Funcion pura: sin I/O, sin estado mutable, determinista.
func SdcliParseProgress(line string) (SdcliProgress, bool) {
	// Formato 1: "  3/30 |  0.84it/s |  10%"
	if m := reProgress1.FindStringSubmatch(line); m != nil {
		step, err1 := strconv.Atoi(m[1])
		total, err2 := strconv.Atoi(m[2])
		itPerSec, err3 := strconv.ParseFloat(m[3], 64)
		pct, err4 := strconv.ParseFloat(m[4], 64)
		if err1 == nil && err2 == nil && err3 == nil && err4 == nil {
			return SdcliProgress{
				Step:       step,
				TotalSteps: total,
				ItPerSec:   itPerSec,
				Percent:    pct,
			}, true
		}
	}

	// Formato 2: "sampling: step 3 of 30 (0.84 it/s)"
	if m := reProgress2.FindStringSubmatch(line); m != nil {
		step, err1 := strconv.Atoi(m[1])
		total, err2 := strconv.Atoi(m[2])
		itPerSec, err3 := strconv.ParseFloat(m[3], 64)
		if err1 == nil && err2 == nil && err3 == nil && total > 0 {
			pct := 100.0 * float64(step) / float64(total)
			return SdcliProgress{
				Step:       step,
				TotalSteps: total,
				ItPerSec:   itPerSec,
				Percent:    pct,
			}, true
		}
	}

	// Formato 3: "step 3/30" o "progress: 3/30" sin velocidad
	if m := reProgress3.FindStringSubmatch(line); m != nil {
		step, err1 := strconv.Atoi(m[1])
		total, err2 := strconv.Atoi(m[2])
		if err1 == nil && err2 == nil && total > 0 {
			pct := 100.0 * float64(step) / float64(total)
			return SdcliProgress{
				Step:       step,
				TotalSteps: total,
				ItPerSec:   0,
				Percent:    pct,
			}, true
		}
	}

	return SdcliProgress{}, false
}
