package selfgame

import "testing"

func TestEvaluateRobotStrategyBlackPoolSkipsRobot(t *testing.T) {
	decision, err := EvaluateRobotStrategy(baseStrategyInput(87, 88), func(int) (int, error) {
		t.Fatal("black pool must not roll strategy probability")
		return 0, nil
	})
	if err != nil {
		t.Fatalf("evaluate strategy failed: %v", err)
	}
	if decision.StrategyCode != StrategyCodePoolGuard || decision.ReasonCode != "POOL_BLACK" || decision.Decision != DecisionNone {
		t.Fatalf("black pool decision mismatch: %+v", decision)
	}
}

func TestEvaluateRobotStrategyBlackPoolCanForceRobotWin(t *testing.T) {
	input := baseStrategyInput(87, 88)
	input.NewUserPolicy.BlackPoolRobotForceWinEnabled = true
	decision, err := EvaluateRobotStrategy(input, func(int) (int, error) {
		t.Fatal("black pool robot force-win must not roll strategy probability")
		return 0, nil
	})
	if err != nil {
		t.Fatalf("evaluate strategy failed: %v", err)
	}
	if decision.StrategyCode != StrategyCodePoolGuard ||
		decision.ReasonCode != "POOL_BLACK_ROBOT_FORCE_WIN" ||
		decision.Decision != DecisionForceHumanLose ||
		decision.ForceResult != ForceResultHumanLose {
		t.Fatalf("black pool force robot win decision mismatch: %+v", decision)
	}
}

func TestEvaluateRobotStrategyDisabledPoolSkipsEvenWhenBlackForceEnabled(t *testing.T) {
	input := baseStrategyInput(200, 88)
	input.StakePool.Status = "disabled"
	input.NewUserPolicy.BlackPoolRobotForceWinEnabled = true
	decision, err := EvaluateRobotStrategy(input, func(int) (int, error) {
		t.Fatal("disabled pool must not roll strategy probability")
		return 0, nil
	})
	if err != nil {
		t.Fatalf("evaluate strategy failed: %v", err)
	}
	if decision.StrategyCode != StrategyCodePoolGuard || decision.ReasonCode != "POOL_BLACK" || decision.Decision != DecisionNone {
		t.Fatalf("disabled pool should still skip robot, got %+v", decision)
	}
}

func TestEvaluateRobotStrategyNewUserProtectionHit(t *testing.T) {
	decision, err := EvaluateRobotStrategy(baseStrategyInput(200, 88), func(max int) (int, error) {
		if max != 100 {
			t.Fatalf("strategy roll max = %d, want 100", max)
		}
		return 0, nil
	})
	if err != nil {
		t.Fatalf("evaluate strategy failed: %v", err)
	}
	if decision.Decision != DecisionForceHumanWin || decision.ForceResult != ForceResultHumanWin || !decision.ShouldConsumeProtection {
		t.Fatalf("new user protection decision mismatch: %+v", decision)
	}
}

func TestEvaluateRobotStrategyNewUserLoseStreakProtection(t *testing.T) {
	input := baseStrategyInput(200, 88)
	input.UserStats.LoseStreak = 2
	decision, err := EvaluateRobotStrategy(input, func(int) (int, error) {
		t.Fatal("lose streak protection must not roll positive-feedback probability")
		return 0, nil
	})
	if err != nil {
		t.Fatalf("evaluate strategy failed: %v", err)
	}
	if decision.Decision != DecisionForceHumanWin || decision.ForceResult != ForceResultHumanWin || !decision.ShouldConsumeProtection {
		t.Fatalf("lose streak protection decision mismatch: %+v", decision)
	}
	if decision.ReasonCode != "NEW_USER_LOSE_STREAK_PROTECTION" {
		t.Fatalf("lose streak protection reason mismatch: %+v", decision)
	}
}

func TestEvaluateRobotStrategyQuotaShortFallsBack(t *testing.T) {
	input := baseStrategyInput(200, 88)
	input.ProtectionState.LifetimeSubsidyCoin = 29950
	decision, err := EvaluateRobotStrategy(input, func(int) (int, error) {
		t.Fatal("quota short must not roll strategy probability")
		return 0, nil
	})
	if err != nil {
		t.Fatalf("evaluate strategy failed: %v", err)
	}
	if decision.Decision != DecisionNone || decision.ReasonCode != "NEW_USER_LIFETIME_QUOTA_SHORT" {
		t.Fatalf("quota short decision mismatch: %+v", decision)
	}
}

func TestEvaluateRobotStrategyOldUserFallsBackToNormalRandom(t *testing.T) {
	input := baseStrategyInput(200, 88)
	input.UserStats = UserStats{EffectiveBotGameCount: 31, WinStreak: 9, TodayNetWinCoin: 900000, SevenDayNetWinCoin: 1800000}
	input.ProtectionState.UsedRounds = 30
	input.NewUserPolicy.NormalPhaseTargetWinRatePercent = 50
	decision, err := EvaluateRobotStrategy(input, nil)
	if err != nil {
		t.Fatalf("evaluate strategy failed: %v", err)
	}
	if decision.Decision != DecisionNone || decision.ForceResult != "" || decision.ReasonCode != "NORMAL_PHASE_TARGET_BASELINE" {
		t.Fatalf("old user must keep normal random decision, got %+v", decision)
	}
}

func TestEvaluateRobotStrategyNormalPhaseDiceTargetHit(t *testing.T) {
	input := baseStrategyInput(200, 88)
	input.UserStats = UserStats{EffectiveBotGameCount: 31}
	input.ProtectionState.UsedRounds = 30
	input.NewUserPolicy.NormalPhaseTargetWinRatePercent = 53
	decision, err := EvaluateRobotStrategy(input, func(max int) (int, error) {
		if max != 10_000 {
			t.Fatalf("normal phase should roll bps, max=%d", max)
		}
		return 599, nil
	})
	if err != nil {
		t.Fatalf("evaluate strategy failed: %v", err)
	}
	if decision.Decision != DecisionForceHumanWin || decision.ForceResult != ForceResultHumanWinWithoutProtection || decision.ShouldConsumeProtection {
		t.Fatalf("normal phase target hit must force win without subsidy consume: %+v", decision)
	}
	if decision.ReasonCode != "NORMAL_PHASE_TARGET_HIT" {
		t.Fatalf("normal phase hit reason mismatch: %+v", decision)
	}
}

func TestEvaluateRobotStrategyNormalPhaseRockTargetMiss(t *testing.T) {
	input := baseStrategyInput(200, 88)
	input.AllowsDraw = true
	input.UserStats = UserStats{EffectiveBotGameCount: 31}
	input.ProtectionState.UsedRounds = 30
	input.NewUserPolicy.NormalPhaseTargetWinRatePercent = 53
	decision, err := EvaluateRobotStrategy(input, func(max int) (int, error) {
		if max != 10_000 {
			t.Fatalf("normal phase should roll bps, max=%d", max)
		}
		return 409, nil
	})
	if err != nil {
		t.Fatalf("evaluate strategy failed: %v", err)
	}
	if decision.Decision != DecisionNone || decision.ForceResult != "" || decision.ReasonCode != "NORMAL_PHASE_TARGET_MISS" {
		t.Fatalf("normal phase target miss must stay random: %+v", decision)
	}
}

func baseStrategyInput(balance int64, required int64) StrategyInput {
	return StrategyInput{
		AppCode:          "lalu",
		GameID:           "dice",
		MatchID:          "match_1",
		UserID:           101,
		StakeCoin:        100,
		RequiredPoolCoin: required,
		StakePool:        StakePool{AppCode: "lalu", GameID: "dice", StakeCoin: 100, BalanceCoin: balance, Status: "active"},
		NewUserPolicy:    DefaultNewUserPolicy("lalu", "dice", 1700000000000),
		ProtectionState:  ProtectionState{AppCode: "lalu", GameID: "dice", UserID: 101, Status: "active"},
	}
}
