using System.Globalization; namespace LehrerApp.Templating; public static class TemplateDataResolver { public static IReadOnlyList ValidateConstants(TemplateManifest manifest) { var issues = new List(); var definitions = manifest.Placeholders.GroupBy(x => x.Name, StringComparer.Ordinal) .ToDictionary(x => x.Key, x => x.First(), StringComparer.Ordinal); foreach (var constant in manifest.Placeholders.Where(x => x.IsConstant)) { try { ConstantValue(constant); if (constant.Type is not (PlaceholderType.Text or PlaceholderType.Multiline)) continue; foreach (var reference in TemplateRichText.UsedPlaceholders(constant.ConstantValue ?? "")) { if (!definitions.TryGetValue(reference, out var target)) issues.Add($"Konstanter Text „{constant.Name}“ verwendet den nicht deklarierten Platzhalter „{reference}“."); else if (target.IsConstant) issues.Add($"Konstanter Text „{constant.Name}“ darf nur externe Platzhalter verwenden; „{reference}“ ist ebenfalls konstant."); else if (target.Type is not (PlaceholderType.Text or PlaceholderType.Multiline or PlaceholderType.Date or PlaceholderType.Number)) issues.Add($"Eingebetteter Platzhalter „{reference}“ hat keinen textuell darstellbaren Typ."); } } catch (InvalidDataException ex) { issues.Add(ex.Message); } } return issues; } public static IReadOnlyDictionary Resolve(TemplateManifest manifest, IReadOnlyDictionary externalValues) { var result = new Dictionary(externalValues, StringComparer.Ordinal); foreach (var definition in manifest.Placeholders.Where(x => x.IsConstant)) result[definition.Name] = ConstantValue(definition); return result; } public static PlaceholderValue ConstantValue(PlaceholderDefinition definition) { var raw = definition.ConstantValue ?? ""; return definition.Type switch { PlaceholderType.Text => new TextValue(raw), PlaceholderType.Multiline => new MultilineValue(raw), PlaceholderType.Date => new DateValue(ParseDate(definition, raw)), PlaceholderType.Number => new NumberValue(ParseNumber(definition, raw)), _ => throw new InvalidDataException( $"Platzhalter „{definition.Name}“ vom Typ {definition.Type} kann keinen konstanten Textwert verwenden."), }; } private static DateOnly ParseDate(PlaceholderDefinition definition, string raw) { if (DateOnly.TryParse(raw, CultureInfo.InvariantCulture, DateTimeStyles.None, out var date) || DateOnly.TryParse(raw, CultureInfo.GetCultureInfo("de-DE"), DateTimeStyles.None, out date)) return date; throw Error(definition, "Datum, z. B. 2026-08-30"); } private static decimal ParseNumber(PlaceholderDefinition definition, string raw) { if (decimal.TryParse(raw, NumberStyles.Number, CultureInfo.InvariantCulture, out var number) || decimal.TryParse(raw, NumberStyles.Number, CultureInfo.GetCultureInfo("de-DE"), out number)) return number; throw Error(definition, "Zahl"); } private static InvalidDataException Error(PlaceholderDefinition definition, string expected) => new($"Konstanter Wert für „{definition.Name}“ ist ungültig; erwartet wird: {expected}."); }