using System.Globalization; using Deal.Modules.Kanban.Application.Models; namespace Deal.Modules.Kanban.Application.ColumnRules; /// /// Человекочитаемое описание правил колонки для note/подсказки. /// public static class RulesDescriber { /// /// Описывает правила колонки. /// /// Правила колонки (null/пустые → «без правил (решает ИИ/ML)»). /// Строка-расшифровка для UI/промпта. public static string Describe(ContainerRulesDto? rules) { if (rules is null) { return NoRulesText; } var parts = new List(); if (rules.Direction.Count > 0) { parts.Add($"направление: {JoinLimited(rules.Direction, DirectionLimit)}"); } if (rules.Stack.Count > 0) { parts.Add($"стек: {JoinLimited(rules.Stack, TermsLimit)}"); } if (rules.Keywords.Count > 0) { parts.Add($"слова: {JoinLimited(rules.Keywords, TermsLimit)}"); } if (rules.Grade.Count > 0) { parts.Add($"грейд: {JoinLimited(rules.Grade, TermsLimit)}"); } if (rules.Levels is { Count: > 0 }) { parts.Add($"уровень: {JoinLimited(rules.Levels, TermsLimit)}"); } if (rules.Locations is { Count: > 0 }) { parts.Add($"локация: {JoinLimited(rules.Locations, TermsLimit)}"); } if (rules.Types is { Count: > 0 }) { parts.Add($"тип: {JoinLimited(rules.Types, TermsLimit)}"); } if (rules.Exclude.Count > 0) { parts.Add($"исключено: {JoinLimited(rules.Exclude, TermsLimit)}"); } AddRangePart(parts, rules.Budget, "бюджет"); AddRangePart(parts, rules.Prices, "цена"); if (parts.Count == 0) { return NoRulesText; } string mode = string.Equals(rules.Mode, ColumnRuleModes.Any, StringComparison.OrdinalIgnoreCase) ? AnyModePrefix : AllModePrefix; return mode + " · " + string.Join("; ", parts); } private const string NoRulesText = "без правил (решает ИИ/ML)"; private const string AllModePrefix = "все условия"; // Префикс режима «любое из условий». private const string AnyModePrefix = "любое из условий"; private const int DirectionLimit = 6; private const int TermsLimit = 8; // Добавляет в описание диапазон группы (бюджет/цена), если задана хотя бы одна граница. // parts: Накопитель частей описания. // range: Диапазон группы (null — группа выключена). // title: Подпись группы («бюджет»/«цена»). private static void AddRangePart( List parts, BudgetRangeDto? range, string title) { if (range is null || (range.From is null && range.To is null)) { return; } string lo = range.From is not null ? range.From.Value.ToString(CultureInfo.InvariantCulture) : string.Empty; string hi = range.To is not null ? range.To.Value.ToString(CultureInfo.InvariantCulture) : string.Empty; string cur = range.Cur ?? string.Empty; parts.Add($"{title}: {lo}–{hi} {cur}".Replace("– ", "–").Replace(" –", "–")); } private static string JoinLimited(IReadOnlyList terms, int limit) { int count = Math.Min(terms.Count, limit); return string.Join(", ", terms.Take(count)); } }