package ana

import (
	"maps"
	"testing"
	"time"

	"gogs.inhome.blapointe.com/ana-ledger/src/ledger"
)

func TestPredictionPredict(t *testing.T) {
	inc := func(b ledger.Balances, _ time.Duration) ledger.Balances {
		result := make(ledger.Balances)
		for k, v := range b {
			result[k] = maps.Clone(v)
			for k2, v2 := range result[k] {
				result[k][k2] = v2 + 1
			}
		}
		return result
	}
	double := func(b ledger.Balances, _ time.Duration) ledger.Balances {
		result := make(ledger.Balances)
		for k, v := range b {
			result[k] = maps.Clone(v)
			for k2, v2 := range result[k] {
				result[k][k2] = v2 * 2
			}
		}
		return result
	}
	from := time.Now()
	month := time.Hour * 24 * 365 / 12
	these := []time.Time{time.Now().Add(time.Hour * 24 * 365 / 12)}
	theseK := these[0].Format("2006-01")
	thisMonth := time.Now().Add(-1 * time.Duration(time.Now().Day()) * time.Hour * 24)
	monthsAgo1 := thisMonth.Add(-1 * month).Format("2006-01")
	monthsAgo2 := thisMonth.Add(-2 * month).Format("2006-01")
	_, _ = inc, double

	cases := map[string]struct {
		prediction Prediction
		given      ledger.Balances
		want       ledger.Register
	}{
		"empty": {
			want: ledger.Register{theseK: {}},
		},
		"double": {
			prediction: Prediction{double},
			given:      ledger.Balances{"X": ledger.Balance{"X": 2}},
			want:       ledger.Register{theseK: ledger.Balances{"X": ledger.Balance{"X": 4}}},
		},
		"inc": {
			prediction: Prediction{inc},
			given:      ledger.Balances{"X": ledger.Balance{"X": 2}},
			want:       ledger.Register{theseK: ledger.Balances{"X": ledger.Balance{"X": 3}}},
		},
		"inc, double": {
			prediction: Prediction{inc, double},
			given:      ledger.Balances{"X": ledger.Balance{"X": 5}},
			want:       ledger.Register{theseK: ledger.Balances{"X": ledger.Balance{"X": 12}}},
		},
		"double, inc": {
			prediction: Prediction{double, inc},
			given:      ledger.Balances{"X": ledger.Balance{"X": 5}},
			want:       ledger.Register{theseK: ledger.Balances{"X": ledger.Balance{"X": 11}}},
		},
		"contribution": {
			prediction: Prediction{
				NewAutoContributionPredictor(ledger.Register{
					"2001-01":  ledger.Balances{"X": ledger.Balance{"X": 100}},   // too old
					"2001-02":  ledger.Balances{"X": ledger.Balance{"X": 10000}}, // too old
					monthsAgo2: ledger.Balances{"X": ledger.Balance{"X": 100}},
					monthsAgo1: ledger.Balances{"X": ledger.Balance{"X": 700}}, // +600 once in 6 months
				}),
			},
			given: ledger.Balances{"X": ledger.Balance{"X": 5}},
			want:  ledger.Register{theseK: ledger.Balances{"X": ledger.Balance{"X": 5}}}, // too few contributions
		},
		"interest": {
			prediction: Prediction{
				NewInterestPredictor("X", "X", 12),
			},
			given: ledger.Balances{"X": ledger.Balance{"X": 5}},
			want:  ledger.Register{theseK: ledger.Balances{"X": ledger.Balance{"X": 10}}},
		},
		"interest, contribution": {
			prediction: Prediction{
				NewInterestPredictor("X", "X", 12),
				NewAutoContributionPredictor(ledger.Register{
					monthsAgo2: ledger.Balances{"X": ledger.Balance{"X": 100}},
					monthsAgo1: ledger.Balances{"X": ledger.Balance{"X": 700}},
				}),
			},
			given: ledger.Balances{"X": ledger.Balance{"X": 5}},
			want:  ledger.Register{theseK: ledger.Balances{"X": ledger.Balance{"X": 10}}}, // too few contributions
		},
		"contribution, interest": {
			prediction: Prediction{
				NewAutoContributionPredictor(ledger.Register{
					monthsAgo2: ledger.Balances{"X": ledger.Balance{"X": 100}},
					monthsAgo1: ledger.Balances{"X": ledger.Balance{"X": 700}},
				}),
				NewInterestPredictor("X", "X", 12),
			},
			given: ledger.Balances{"X": ledger.Balance{"X": 5}},
			want:  ledger.Register{theseK: ledger.Balances{"X": ledger.Balance{"X": 10}}}, // too few contributions
		},
	}

	for name, d := range cases {
		c := d
		t.Run(name, func(t *testing.T) {
			got := c.prediction.predict(c.given, from, these)
			if got.Debug() != c.want.Debug() {
				t.Errorf("want\n\t%+v, got\n\t%+v", c.want.Debug(), got.Debug())
			}
		})
	}
}
