package stats

import (
	"encoding/json"
	"fmt"
	"math"
	"os"
	"strconv"
	"strings"
	"time"
)

const DefaultPath = "data/stats.json"

type Metric struct {
	Value       int64   `json:"value"`
	Status      string  `json:"status"`
	StartedAt   string  `json:"started_at,omitempty"`
	FinishedAt  string  `json:"finished_at,omitempty"`
	DurationSec float64 `json:"duration_sec,omitempty"`
	Error       string  `json:"error,omitempty"`
}

type File struct {
	UpdatedAt string            `json:"updated_at"`
	Metrics   map[string]Metric `json:"metrics"`
}

type Display struct {
	Main   string
	Suffix string
}

func Load(path string) (File, error) {
	if path == "" {
		path = DefaultPath
	}
	data, err := os.ReadFile(path)
	if err != nil {
		return File{}, err
	}
	var f File
	if err := json.Unmarshal(data, &f); err != nil {
		return File{}, err
	}
	if f.Metrics == nil {
		f.Metrics = make(map[string]Metric)
	}
	return f, nil
}

func (f File) Value(key string) (int64, bool) {
	m, ok := f.Metrics[key]
	if !ok || m.Status != "done" || m.Value < 0 {
		return 0, false
	}
	return m.Value, true
}

func FormatCounter(n int64) Display {
	if n >= 1_000_000 {
		millions := float64(n) / 1_000_000
		whole := int(math.Floor(millions + 1e-9))
		frac := int(math.Round((millions - float64(whole)) * 10))
		if frac == 10 {
			whole++
			frac = 0
		}
		if frac > 0 {
			return Display{Main: strconv.Itoa(whole), Suffix: fmt.Sprintf(",%dM+", frac)}
		}
		return Display{Main: strconv.Itoa(whole), Suffix: "M+"}
	}
	return Display{Main: strconv.FormatInt(n, 10), Suffix: ""}
}

func FormatMillionsLabel(n int64) string {
	if n >= 1_000_000 {
		millions := float64(n) / 1_000_000
		whole := int(math.Floor(millions + 1e-9))
		frac := int(math.Round((millions - float64(whole)) * 10))
		if frac == 10 {
			whole++
			frac = 0
		}
		if frac > 0 {
			return fmt.Sprintf("%d,%d milhões de empresas", whole, frac)
		}
		return fmt.Sprintf("%d milhões de empresas", whole)
	}
	if n >= 1_000 {
		return fmt.Sprintf("%s mil empresas", formatThousandsPT(n))
	}
	return fmt.Sprintf("%d empresas", n)
}

func formatThousandsPT(n int64) string {
	s := strconv.FormatInt(n, 10)
	if len(s) <= 3 {
		return s
	}
	var parts []string
	for len(s) > 3 {
		parts = append([]string{s[len(s)-3:]}, parts...)
		s = s[:len(s)-3]
	}
	if s != "" {
		parts = append([]string{s}, parts...)
	}
	return strings.Join(parts, ".")
}

func CounterHTML(n int64) string {
	d := FormatCounter(n)
	if d.Suffix == "" {
		return fmt.Sprintf(`<span class="counter">%s</span>`, d.Main)
	}
	return fmt.Sprintf(`<span class="counter">%s</span>%s`, d.Main, d.Suffix)
}

func ApplyHomeHTML(html string, f File, fallbacks map[string]int64) string {
	apply := func(metricKey, fallbackKey string) string {
		v, ok := f.Value(metricKey)
		if !ok {
			v = fallbacks[fallbackKey]
		}
		return CounterHTML(v)
	}

	statKeys := []struct {
		metric   string
		fallback string
	}{
		{"empresas_indice", "empresas_indice"},
		{"com_telefone", "com_telefone"},
		{"com_email", "com_email"},
		{"socios_cadastrados", "socios_cadastrados"},
		{"estados_uf", "estados_uf"},
	}
	for _, sk := range statKeys {
		html = replaceStatBlock(html, sk.metric, apply(sk.metric, sk.fallback))
	}

	banner := fallbacks["empresas_indice"]
	if v, ok := f.Value("empresas_indice"); ok {
		banner = v
	}
	html = replaceStatBlock(html, "banner_empresas", FormatMillionsLabel(banner))

	return html
}

func replaceStatBlock(html, key, value string) string {
	start := "<!-- LEADB2B_STAT:" + key + " -->"
	end := "<!-- /LEADB2B_STAT:" + key + " -->"
	for {
		i := strings.Index(html, start)
		if i < 0 {
			return html
		}
		j := strings.Index(html[i:], end)
		if j < 0 {
			return html
		}
		j += i + len(end)
		html = html[:i] + value + html[j:]
	}
}

func DefaultFallbacks() map[string]int64 {
	return map[string]int64{
		"empresas_indice":     12_400_000,
		"com_telefone":        8_200_000,
		"com_email":           3_100_000,
		"socios_cadastrados":  18_000_000,
		"estados_uf":          27,
	}
}

func (f File) UpdatedAtTime() time.Time {
	if f.UpdatedAt == "" {
		return time.Time{}
	}
	t, err := time.Parse(time.RFC3339, f.UpdatedAt)
	if err != nil {
		return time.Time{}
	}
	return t
}
