package dashboard

import (
	"context"
	"time"

	"github.com/go-logr/logr"
	"github.com/tidbcloud/devops/entity"

	. "github.com/onsi/ginkgo/v2"
	. "github.com/onsi/gomega"
)

var _ = Describe("SLACache", func() {
	var (
		cache           *SLACache
		fixedNow        time.Time
		dedicatedData   map[string]slaInfo
		nextGenData     map[string]slaInfo
		serverlessData  map[string]slaInfo
		dedicatedCalls  [][]string
		nextGenCalls    [][]string
		serverlessCalls [][]string
	)

	newLoader := func(data map[string]slaInfo, calls *[][]string) slaMonthlyLoader {
		return func(_ context.Context, months []string) (map[string]slaInfo, error) {
			recorded := append([]string(nil), months...)
			*calls = append(*calls, recorded)

			result := make(map[string]slaInfo)
			for _, month := range months {
				if item, ok := data[month]; ok {
					result[month] = item
				}
			}
			return result, nil
		}
	}

	findTrendValue := func(trends []trendKey, month string, series string) int {
		for _, trend := range trends {
			if trend.Month == month && trend.Series == series {
				return trend.Value
			}
		}
		return -1
	}

	BeforeEach(func() {
		fixedNow = time.Date(2026, time.April, 6, 9, 0, 0, 0, time.UTC)
		dedicatedCalls = nil
		nextGenCalls = nil
		serverlessCalls = nil

		dedicatedData = map[string]slaInfo{
			"2025/5":  {Month: "2025/5", Perfect: 1, Warning: 0, Breach: 0},
			"2025/6":  {Month: "2025/6", Perfect: 2, Warning: 0, Breach: 0},
			"2025/7":  {Month: "2025/7", Perfect: 3, Warning: 0, Breach: 0},
			"2025/8":  {Month: "2025/8", Perfect: 4, Warning: 0, Breach: 0},
			"2025/9":  {Month: "2025/9", Perfect: 5, Warning: 0, Breach: 0},
			"2025/10": {Month: "2025/10", Perfect: 6, Warning: 0, Breach: 0},
			"2025/11": {Month: "2025/11", Perfect: 7, Warning: 0, Breach: 0},
			"2025/12": {Month: "2025/12", Perfect: 8, Warning: 0, Breach: 0},
			"2026/1":  {Month: "2026/1", Perfect: 9, Warning: 0, Breach: 0},
			"2026/2":  {Month: "2026/2", Perfect: 10, Warning: 1, Breach: 0},
			"2026/3":  {Month: "2026/3", Perfect: 11, Warning: 1, Breach: 1},
			"2026/4":  {Month: "2026/4", Perfect: 12, Warning: 2, Breach: 2},
		}
		nextGenData = map[string]slaInfo{
			"2025/5":  {Month: "2025/5", Perfect: 2, Warning: 0, Breach: 0},
			"2025/6":  {Month: "2025/6", Perfect: 3, Warning: 0, Breach: 0},
			"2025/7":  {Month: "2025/7", Perfect: 4, Warning: 0, Breach: 0},
			"2025/8":  {Month: "2025/8", Perfect: 5, Warning: 0, Breach: 0},
			"2025/9":  {Month: "2025/9", Perfect: 6, Warning: 0, Breach: 0},
			"2025/10": {Month: "2025/10", Perfect: 7, Warning: 0, Breach: 0},
			"2025/11": {Month: "2025/11", Perfect: 8, Warning: 0, Breach: 0},
			"2025/12": {Month: "2025/12", Perfect: 9, Warning: 0, Breach: 0},
			"2026/1":  {Month: "2026/1", Perfect: 10, Warning: 0, Breach: 0},
			"2026/2":  {Month: "2026/2", Perfect: 5, Warning: 5, Breach: 0},
			"2026/3":  {Month: "2026/3", Perfect: 7, Warning: 2, Breach: 1},
			"2026/4":  {Month: "2026/4", Perfect: 8, Warning: 1, Breach: 1},
		}
		serverlessData = map[string]slaInfo{
			"2025/5":  {Month: "2025/5", Perfect: 20, Warning: 1, Breach: 0},
			"2025/6":  {Month: "2025/6", Perfect: 21, Warning: 1, Breach: 0},
			"2025/7":  {Month: "2025/7", Perfect: 22, Warning: 1, Breach: 0},
			"2025/8":  {Month: "2025/8", Perfect: 23, Warning: 1, Breach: 0},
			"2025/9":  {Month: "2025/9", Perfect: 24, Warning: 1, Breach: 0},
			"2025/10": {Month: "2025/10", Perfect: 25, Warning: 1, Breach: 0},
			"2025/11": {Month: "2025/11", Perfect: 26, Warning: 1, Breach: 0},
			"2025/12": {Month: "2025/12", Perfect: 27, Warning: 1, Breach: 0},
			"2026/1":  {Month: "2026/1", Perfect: 28, Warning: 1, Breach: 0},
			"2026/2":  {Month: "2026/2", Perfect: 18, Warning: 1, Breach: 1},
			"2026/3":  {Month: "2026/3", Perfect: 15, Warning: 2, Breach: 3},
			"2026/4":  {Month: "2026/4", Perfect: 14, Warning: 2, Breach: 4},
		}

		interval := time.Hour
		cache = newSLACache(
			logr.Discard(),
			&interval,
			nil,
			nil,
			&slaCacheLoaders{
				dedicated:  newLoader(dedicatedData, &dedicatedCalls),
				nextGen:    newLoader(nextGenData, &nextGenCalls),
				serverless: newLoader(serverlessData, &serverlessCalls),
			},
		)
		cache.now = func() time.Time { return fixedNow }
	})

	It("preloads the latest year and returns the requested months from cache", func() {
		info, err := cache.Get(context.Background(), []string{"2026/2", "2026/3"})
		Expect(err).ToNot(HaveOccurred())

		expectedRecentMonths := []string{
			"2025/4", "2025/5", "2025/6", "2025/7", "2025/8", "2025/9", "2025/10",
			"2025/11", "2025/12", "2026/1", "2026/2", "2026/3", "2026/4",
		}
		Expect(dedicatedCalls).To(Equal([][]string{expectedRecentMonths}))
		Expect(nextGenCalls).To(Equal([][]string{expectedRecentMonths}))
		Expect(serverlessCalls).To(Equal([][]string{expectedRecentMonths}))

		Expect(info.DedicatedSLA.Current).To(BeNumerically("~", 0.923, 0.001))
		Expect(info.NextGenSLA.Current).To(BeNumerically("~", 0.9, 0.001))
		Expect(info.ServerlessSLA.Current).To(BeNumerically("~", 0.85, 0.001))

		Expect(extractTrendKeys(info.DedicatedSLA.MonthlyTrends)).To(HaveLen(6))
		Expect(findTrendValue(extractTrendKeys(info.DedicatedSLA.MonthlyTrends), "2026/2", "perfect")).To(Equal(10))
		Expect(findTrendValue(extractTrendKeys(info.DedicatedSLA.MonthlyTrends), "2026/3", "breach")).To(Equal(1))
	})

	It("refreshes only the current month after the initial preload", func() {
		_, err := cache.Get(context.Background(), []string{"2026/3", "2026/4"})
		Expect(err).ToNot(HaveOccurred())

		dedicatedData["2026/3"] = slaInfo{Month: "2026/3", Perfect: 99, Warning: 0, Breach: 0}
		dedicatedData["2026/4"] = slaInfo{Month: "2026/4", Perfect: 3, Warning: 3, Breach: 4}
		nextGenData["2026/3"] = slaInfo{Month: "2026/3", Perfect: 50, Warning: 0, Breach: 0}
		nextGenData["2026/4"] = slaInfo{Month: "2026/4", Perfect: 4, Warning: 4, Breach: 2}
		serverlessData["2026/3"] = slaInfo{Month: "2026/3", Perfect: 30, Warning: 0, Breach: 0}
		serverlessData["2026/4"] = slaInfo{Month: "2026/4", Perfect: 12, Warning: 3, Breach: 5}

		err = cache.syncCurrentMonth(context.Background())
		Expect(err).ToNot(HaveOccurred())

		Expect(dedicatedCalls).To(HaveLen(2))
		Expect(nextGenCalls).To(HaveLen(2))
		Expect(serverlessCalls).To(HaveLen(2))
		Expect(dedicatedCalls[1]).To(Equal([]string{"2026/4"}))
		Expect(nextGenCalls[1]).To(Equal([]string{"2026/4"}))
		Expect(serverlessCalls[1]).To(Equal([]string{"2026/4"}))

		info, err := cache.Get(context.Background(), []string{"2026/3", "2026/4"})
		Expect(err).ToNot(HaveOccurred())

		dedicatedTrends := extractTrendKeys(info.DedicatedSLA.MonthlyTrends)
		Expect(findTrendValue(dedicatedTrends, "2026/3", "perfect")).To(Equal(11))
		Expect(findTrendValue(dedicatedTrends, "2026/4", "perfect")).To(Equal(3))

		nextGenTrends := extractTrendKeys(info.NextGenSLA.MonthlyTrends)
		Expect(findTrendValue(nextGenTrends, "2026/3", "perfect")).To(Equal(7))
		Expect(findTrendValue(nextGenTrends, "2026/4", "warning")).To(Equal(4))

		serverlessTrends := extractTrendKeys(info.ServerlessSLA.MonthlyTrends)
		Expect(findTrendValue(serverlessTrends, "2026/3", "perfect")).To(Equal(15))
		Expect(findTrendValue(serverlessTrends, "2026/4", "breach")).To(Equal(5))
	})
})

type trendKey struct {
	Month  string
	Series string
	Value  int
}

func extractTrendKeys(trends []entity.SeriesMonthlyTrend) []trendKey {
	result := make([]trendKey, 0, len(trends))
	for _, trend := range trends {
		result = append(result, trendKey{
			Month:  trend.Month,
			Series: trend.Series,
			Value:  trend.Value,
		})
	}
	return result
}
