using Core.Entities;
using Core.Models;
using FuzzySharp;
namespace GoogleSheetsScheduleImport;
///
/// Maps a sheet cell title to the closest event definition (fuzzy) plus MS/HS for section headers.
///
public static class OccurrenceEventMatcher
{
private const int MinTokenScore = 62;
public static bool TryMatch(
string occurrenceName,
IReadOnlyList events,
out EventDefinition? matched,
out SchoolLevel? level)
{
matched = null;
level = null;
if (string.IsNullOrWhiteSpace(occurrenceName) || events.Count == 0)
return false;
var normalized = TextNormalization.ForEmitLine(occurrenceName);
var (remainder, prefixLevel) = SchoolLevelPrefixParser.StripLeadingSchoolPrefix(normalized);
// Section headers require "Event Name - MS|HS". Titles without a clear school prefix stay in General Schedule.
if (!prefixLevel.HasValue)
return false;
var best = FindBest(remainder, events);
if (best == null || string.IsNullOrWhiteSpace(remainder))
return false;
matched = best;
level = prefixLevel;
return true;
}
private static EventDefinition? FindBest(string text, IReadOnlyList events)
{
if (string.IsNullOrWhiteSpace(text))
return null;
EventDefinition? best = null;
var bestScore = 0;
foreach (var e in events)
{
if (string.IsNullOrWhiteSpace(e.Name))
continue;
var score = Math.Max(
Fuzz.TokenSetRatio(text, e.Name),
Fuzz.PartialRatio(text, e.Name));
if (score > bestScore)
{
bestScore = score;
best = e;
}
else if (score == bestScore && best != null && e.Name.Length > best.Name.Length)
best = e;
}
if (best == null || bestScore < MinTokenScore)
return null;
return best;
}
}