using SVSim.Database.Models;
namespace SVSim.EmulatedEntrypoint.Services;
///
/// Pure service — maps the puzzle mission catalog against a viewer's cleared-puzzle set and
/// produces per-mission (total_count, is_achieved) statuses. Used by both /basic_puzzle/mission
/// (snapshot) and /basic_puzzle/finish (post-clear delta detection).
///
public sealed class PuzzleMissionEvaluator
{
public sealed record MissionStatus(PuzzleMissionEntry Mission, int TotalCount, bool IsAchieved);
public IReadOnlyList Evaluate(
IEnumerable catalog,
IReadOnlyDictionary> clearedByGroup)
{
var result = new List();
foreach (var mission in catalog)
{
int count = ComputeTotalCount(mission, clearedByGroup);
result.Add(new MissionStatus(mission, count, count >= mission.RequireNumber));
}
return result;
}
/// Returns ONLY the missions whose status flipped from not-achieved to achieved
/// between before and after. Other missions (already-achieved, still-incomplete) are omitted.
public IReadOnlyList FreshlyCompleted(
IEnumerable catalog,
IReadOnlyDictionary> clearedByGroupBefore,
IReadOnlyDictionary> clearedByGroupAfter)
{
var result = new List();
foreach (var mission in catalog)
{
int before = ComputeTotalCount(mission, clearedByGroupBefore);
int after = ComputeTotalCount(mission, clearedByGroupAfter);
bool wasAchieved = before >= mission.RequireNumber;
bool isAchieved = after >= mission.RequireNumber;
if (!wasAchieved && isAchieved)
result.Add(new MissionStatus(mission, after, true));
}
return result;
}
private static int ComputeTotalCount(PuzzleMissionEntry mission, IReadOnlyDictionary> clearedByGroup)
{
if (mission.TargetPuzzleGroupId is not int groupId) return 0;
if (!clearedByGroup.TryGetValue(groupId, out var cleared)) return 0;
return Math.Min(cleared.Count, mission.RequireNumber);
}
}