using System;

namespace PolyTraderSharp.Services
{
    /// <summary>
    /// Anzeige-Zeitzone der Anwendung. Wird beim Start EINMAL aus den Server-Settings gesetzt
    /// (<c>ApplicationTimeZoneId</c>) und danach überall zum Umrechnen von UTC-Zeitstempeln in die
    /// dargestellte Ortszeit verwendet.
    ///
    /// <para><b>Warum das nötig war:</b> Die Terminal-Ansicht rechnete bisher hart gegen
    /// <c>"W. Europe Standard Time"</c> – eine <i>Windows</i>-Zeitzonen-ID. Auf Linux funktioniert die
    /// nur über die ICU-Zuordnung und fällt komplett aus, wenn ICU fehlt oder
    /// <c>InvariantGlobalization</c> gesetzt ist. Dann hätte das Fenster beim Öffnen geworfen.</para>
    ///
    /// <para><b>Auflösung:</b> Zuerst die konfigurierte ID direkt, dann über die Umrechnung zwischen
    /// IANA- und Windows-Schreibweise (damit dieselbe Konfiguration auf beiden Plattformen trägt),
    /// zuletzt die Zeitzone des Systems. Ein Fehlschlag ist damit nie fatal – er wird nur gemeldet.</para>
    ///
    /// <para>Die Zeitzone wird bewusst einmalig bei der Installation festgelegt und nicht im
    /// laufenden Betrieb gewechselt: eine Änderung verschiebt Logdatei-Tagesgrenzen und
    /// Auswertungszeiträume. Änderungen greifen deshalb erst nach einem Neustart.</para>
    /// </summary>
    public static class AppTimeZone
    {
        /// <summary>Empfehlung für neue Installationen – IANA-Schreibweise, funktioniert auf beiden Plattformen.</summary>
        public const string DefaultId = "Europe/Berlin";

        /// <summary>Aktuell gültige Anzeige-Zeitzone. Vor <see cref="Configure"/> die des Systems.</summary>
        public static TimeZoneInfo Current { get; private set; } = TimeZoneInfo.Local;

        /// <summary>Die tatsächlich verwendete ID (kann von der gewünschten abweichen, wenn ausgewichen wurde).</summary>
        public static string CurrentId => Current.Id;

        /// <summary>
        /// Setzt die Anzeige-Zeitzone. Leere Angabe = Systemzeitzone. Meldet über
        /// <paramref name="warn"/>, wenn auf etwas anderes als das Gewünschte ausgewichen wurde.
        /// </summary>
        public static void Configure(string? timeZoneId, Action<string>? warn = null)
        {
            if (string.IsNullOrWhiteSpace(timeZoneId))
            {
                Current = TimeZoneInfo.Local;
                return;
            }

            string id = timeZoneId.Trim();
            if (TryFind(id, out var tz)) { Current = tz!; return; }

            // Andere Schreibweise versuchen: eine unter Windows gepflegte Konfiguration soll auch
            // auf Linux tragen und umgekehrt.
            if (TimeZoneInfo.TryConvertIanaIdToWindowsId(id, out string? windowsId)
                && TryFind(windowsId!, out tz))
            {
                Current = tz!;
                warn?.Invoke($"Zeitzone „{id}\" wurde als „{tz!.Id}\" aufgelöst.");
                return;
            }

            if (TimeZoneInfo.TryConvertWindowsIdToIanaId(id, out string? ianaId)
                && TryFind(ianaId!, out tz))
            {
                Current = tz!;
                warn?.Invoke($"Zeitzone „{id}\" wurde als „{tz!.Id}\" aufgelöst.");
                return;
            }

            Current = TimeZoneInfo.Local;
            warn?.Invoke($"⚠️ Zeitzone „{id}\" ist auf diesem System unbekannt – es wird die Systemzeitzone " +
                         $"„{TimeZoneInfo.Local.Id}\" verwendet. Empfohlen ist die IANA-Schreibweise, z.B. „{DefaultId}\".");
        }

        /// <summary>Rechnet einen Zeitstempel in die Anzeige-Zeitzone um (UTC-Kennzeichnung wird beachtet).</summary>
        public static DateTime ToDisplay(DateTime value) =>
            value.Kind == DateTimeKind.Utc
                ? TimeZoneInfo.ConvertTimeFromUtc(value, Current)
                : TimeZoneInfo.ConvertTime(value, Current);

        private static bool TryFind(string id, out TimeZoneInfo? tz)
        {
            try { tz = TimeZoneInfo.FindSystemTimeZoneById(id); return true; }
            catch (TimeZoneNotFoundException) { tz = null; return false; }
            catch (InvalidTimeZoneException) { tz = null; return false; }
        }
    }
}
