package plugin

import (
	"testing"
	"time"

	"maunium.net/go/mautrix/id"
)

// L1: babysit pivot from harvest to pet-care + safe-rest.
// These tests cover the pure pieces — DB-touching paths exercise via
// integration only.

func TestBabysitSafeRest_NoDB_ReturnsFalse(t *testing.T) {
	// Tests run without db.Init(); BabysitSafeRest must recover and
	// return false rather than panicking.
	if BabysitSafeRest(id.UserID("@nodb:example")) {
		t.Errorf("expected false for un-initialized DB")
	}
}

func TestRunBabysitDailyTrickle_NoPetGrantsNothing(t *testing.T) {
	// Without a pet, the trickle is a logging-only no-op for character
	// state. Without DB, the log call panics inside db.Get; recover and
	// assert that the in-memory char fields stay clean.
	defer func() { _ = recover() }()

	expires := time.Now().UTC().Add(7 * 24 * time.Hour)
	char := &AdventureCharacter{
		UserID:           id.UserID("@nopet:example"),
		BabysitActive:    true,
		BabysitExpiresAt: &expires,
	}
	p := &AdventurePlugin{}
	p.runBabysitDailyTrickle(char)
	if char.PetXP != 0 {
		t.Errorf("char without pet should not gain PetXP, got %d", char.PetXP)
	}
	if char.PetLevel != 0 {
		t.Errorf("char without pet should not change PetLevel, got %d", char.PetLevel)
	}
}

func TestRunBabysitDailyTrickle_SkipsWhenInactive(t *testing.T) {
	defer func() { _ = recover() }()
	char := &AdventureCharacter{
		UserID:        id.UserID("@inactive:example"),
		BabysitActive: false,
		PetType:       "dog",
		PetName:       "Rex",
		PetLevel:      1,
		PetXP:         0,
	}
	p := &AdventurePlugin{}
	p.runBabysitDailyTrickle(char)
	if char.PetXP != 0 {
		t.Errorf("inactive babysit should grant no XP, got %d", char.PetXP)
	}
}

func TestRunBabysitDailyTrickle_AccumulatesAndLevels(t *testing.T) {
	// Pet at L9 with 49 XP needed → 1 day at +3 trickle should *not* level
	// (49→52 in centi-XP / 100 = no, wait: petXPToNextLevel at L9 = 50 ×
	// 100 = 5000 centi-XP). Verify we accumulate centi-XP correctly across
	// multiple daily ticks and eventually level up.
	defer func() { _ = recover() }()

	char := &AdventureCharacter{
		UserID:        id.UserID("@accum:example"),
		PetType:       "dog",
		PetName:       "Rex",
		PetLevel:      1, // needs 10 XP = 1000 centi-XP to L2
		PetXP:         900,
		BabysitActive: true,
	}
	expires := time.Now().UTC().Add(48 * time.Hour)
	char.BabysitExpiresAt = &expires
	p := &AdventurePlugin{}

	// One trickle adds 3*100 = 300 centi-XP. 900 + 300 = 1200 >= 1000 →
	// level to 2, carry 200 centi-XP.
	p.runBabysitDailyTrickle(char)
	if char.PetLevel != 2 {
		t.Errorf("expected level 2 after trickle, got %d (xp=%d)", char.PetLevel, char.PetXP)
	}
	if char.PetXP != 200 {
		t.Errorf("expected 200 carryover centi-XP, got %d", char.PetXP)
	}
}

func TestBabysitLogStats_CountsPetCareDays(t *testing.T) {
	logs := []babysitLogEntry{
		{Activity: "pet_care", XPGained: 3},
		{Activity: "pet_care", XPGained: 3},
		{Activity: "pet_care", XPGained: 3, RivalRefused: ""},
		{Activity: "rival_refused", RivalRefused: "Garth"},
	}
	xp, days, rivals := babysitLogStats(logs)
	if days != 3 {
		t.Errorf("petDays = %d, want 3", days)
	}
	if xp != 9 {
		t.Errorf("totalXP = %d, want 9", xp)
	}
	if rivals != 1 {
		t.Errorf("rivalsRefused = %d, want 1", rivals)
	}
}
