using Elternbeirat.Contracts;
using Elternbeirat.Web.Shared;
namespace Elternbeirat.Web.Features.Events;
///
/// Formats the date range of an for display in German.
///
///
/// Shared by the full event list, the home-page events embed and the next-event
/// card, so all render dates the same way: as the full date,
/// the Sheet… methods and for the calendar sheet and the
/// time line beside it, and for the list of past events.
///
/// This is the event counterpart to
/// , which formats a plain
/// day instead of a point-in-time range.
///
///
public static class EventDateFormat
{
///
/// Date format for the start, and for a single-day event.
///
private const string LongDate = "dddd, d. MMMM yyyy";
///
/// Date format for the end of a multi-day event; the weekday is omitted.
///
private const string ShortDate = "d. MMMM yyyy";
///
/// Date format for the list of past events, which is grouped by year.
///
private const string DayMonth = "d. MMMM";
///
/// Formats the date range of an event.
///
///
/// The event to format.
///
///
/// The formatted range, depending on and :
///
/// -
/// No end: Donnerstag, 8. Oktober 2026, 19:30 Uhr
///
/// -
/// Same day: Donnerstag, 8. Oktober 2026, 19:30–21:00 Uhr
///
/// -
/// Several days: Freitag, 9. Oktober 2026 – 11. Oktober 2026
///
///
/// Without a time, the time parts are omitted.
///
public static string Range(Event @event)
=> (@event.Start, @event.End, @event.HasTime) switch
{
(var start, null, var hasTime)
=> Stamp(start, LongDate, hasTime),
(var start, { } end, true) when end.Date == start.Date
=> $"{start.ToString(LongDate, Cultures.German)}, {Clock(start)}–{Clock(end)} Uhr",
(var start, { } end, false) when end.Date == start.Date
=> Stamp(start, LongDate, withTime: false),
(var start, { } end, var hasTime)
=> $"{Stamp(start, LongDate, hasTime)} – {Stamp(end, ShortDate, hasTime)}",
};
///
/// The short month for the top band of a calendar sheet.
///
/// The event's start.
/// The German short month without its period, e.g. Okt or März.
///
/// Returned in normal case; the sheet's CSS sets it in capitals. The period of the
/// abbreviation (Okt.) is dropped because on a sheet it reads as a stray dot.
///
///
public static string SheetMonth(DateTime value)
=> value.ToString("MMM", Cultures.German).TrimEnd('.');
///
/// The day of the month for the large number on a calendar sheet.
///
/// The event's start.
/// The day without a leading zero, e.g. 8.
public static string SheetDay(DateTime value)
=> value.Day.ToString(Cultures.German);
///
/// The short weekday for the bottom of a calendar sheet.
///
/// The event's start.
/// The German short weekday without its period, e.g. Do.
public static string SheetWeekday(DateTime value)
=> value.ToString("ddd", Cultures.German).TrimEnd('.');
///
/// The time line beside a calendar sheet: what the sheet itself cannot show.
///
/// The event to format.
///
/// Depending on and :
///
/// - No end: 19:30 Uhr
/// - Same day: 19:30–21:00 Uhr
/// - Several days: 09:00 Uhr bis Sonntag, 11. Oktober 2026, 13:00 Uhr
/// - Several days, no time: bis Sonntag, 11. Oktober 2026
/// - One day, no time: an empty string; the sheet says it all.
///
///
///
/// The sheet shows the start day only, so a multi-day event must name its last
/// day here, with the weekday, as the sheet does for the first one.
///
public static string Span(Event @event)
=> (@event.Start, @event.End, @event.HasTime) switch
{
(var start, { } end, var hasTime) when end.Date != start.Date
=> (hasTime ? Clock(start) + " Uhr " : "") + "bis " + Stamp(end, LongDate, hasTime),
(var start, null, true)
=> $"{Clock(start)} Uhr",
(var start, { } end, true)
=> $"{Clock(start)}–{Clock(end)} Uhr",
_ => "",
};
///
/// The day column of a past event in the folded-up list on the events page.
///
/// The event to format.
///
/// Day and full month, depending on :
///
/// - One day: 13. November
/// - Several days in one month: 8.–9. Februar
/// - Across a month: 30. Januar – 2. Februar
/// - Across a year: 30. Dezember – 2. Januar 2027
///
///
///
/// The list is grouped by the start year under its own small heading, so the
/// year is only named where the end leaves that year. The weekday is left out
/// to keep the column short; the time follows the title ().
///
public static string PastDays(Event @event)
=> (@event.Start, @event.End) switch
{
(var start, { } end) when end.Year != start.Year
=> $"{start.ToString(DayMonth, Cultures.German)} – {end.ToString(ShortDate, Cultures.German)}",
(var start, { } end) when end.Month != start.Month
=> $"{start.ToString(DayMonth, Cultures.German)} – {end.ToString(DayMonth, Cultures.German)}",
(var start, { } end) when end.Day != start.Day
=> $"{start.Day.ToString(Cultures.German)}.–{end.ToString(DayMonth, Cultures.German)}",
(var start, _)
=> start.ToString(DayMonth, Cultures.German),
};
///
/// The time after the title of a past event in the folded-up list.
///
/// The event to format.
///
/// Depending on and :
///
/// - No time: an empty string; nothing is shown.
/// - No end: 19:00 Uhr
/// - Same day: 19:00–21:00 Uhr
/// - Several days: the start time only, 09:00 Uhr
///
///
///
/// For several days the day column already shows the span; a time range beside
/// it would read as the daily hours, so only the start time is given.
///
public static string PastTime(Event @event)
=> (@event.HasTime, @event.End) switch
{
(false, _) => "",
(true, { } end) when end.Date != @event.Start.Date => $"{Clock(@event.Start)} Uhr",
_ => Span(@event),
};
///
/// Formats the clock time of a point in time.
///
/// The date and time.
/// The time as e.g. 19:30.
private static string Clock(DateTime value)
=> value.ToString("HH:mm", Cultures.German);
///
/// Formats a single point in time as a date with an optional time.
///
///
/// The date and time to format.
///
///
/// The date format, e.g. .
///
///
/// to append the time, e.g. , 19:30 Uhr.
///
///
/// The formatted date, with the time if requested.
///
private static string Stamp(DateTime value, string dateFormat, bool withTime)
=> withTime
? value.ToString(dateFormat + ", HH:mm", Cultures.German) + " Uhr"
: value.ToString(dateFormat, Cultures.German);
}