using System.Collections.ObjectModel;
using System.Windows.Input;
using Avalonia.Threading;
using Ehwrj.App.Models;
using Ehwrj.App.Services;
using Ehwrj.Core.Models;

namespace Ehwrj.App.ViewModels;

public sealed class MainViewModel : ObservableObject, IDisposable
{
    private readonly LiveMapService _service;
    private readonly SettingsStore _settingsStore;
    private LiveSnapshot _snapshot = LiveSnapshot.Empty;
    private bool _isOverlayEnabled;
    private int _lastAllyCount = -1;
    private int _lastEnemyCount = -1;
    private DateTimeOffset? _sessionStartedAt;
    private readonly HashSet<string> _seenMessages = new(StringComparer.Ordinal);

    public MainViewModel(LiveMapService service, SettingsStore settingsStore, AppSettings settings)
    {
        _service = service;
        _settingsStore = settingsStore;
        Settings = settings;

        StartCommand = new RelayCommand(Start);
        StopCommand = new RelayCommand(Stop);
        SaveCommand = new RelayCommand(Save);
        ResetSettingsCommand = new RelayCommand(ResetSettings);

        _service.SnapshotUpdated += OnSnapshotUpdated;
        UpdateEndpointHealth(Snapshot);
    }

    public AppSettings Settings { get; }
    public ICommand StartCommand { get; }
    public ICommand StopCommand { get; }
    public ICommand SaveCommand { get; }
    public ICommand ResetSettingsCommand { get; }
    public ObservableCollection<string> BattleLogEntries { get; } = new();
    public ObservableCollection<string> EndpointHealthEntries { get; } = new();
    public IReadOnlyList<LanguageOption> LanguageOptions => UiText.LanguageOptions;
    public UiText Ui => UiText.For(Settings.Language);

    public LanguageOption SelectedLanguageOption
    {
        get => LanguageOptions.FirstOrDefault(o => string.Equals(o.Code, Settings.Language, StringComparison.OrdinalIgnoreCase))
               ?? LanguageOptions[0];
        set
        {
            if (string.Equals(Settings.Language, value.Code, StringComparison.OrdinalIgnoreCase))
            {
                return;
            }

            Settings.Language = value.Code;
            OnPropertyChanged();
            OnPropertyChanged(nameof(Ui));
            NotifyLocalizedProperties();
            RebuildBattleLog();
            Save();
        }
    }

    public LiveSnapshot Snapshot
    {
        get => _snapshot;
        private set
        {
            if (SetProperty(ref _snapshot, value))
            {
                OnPropertyChanged(nameof(Status));
                OnPropertyChanged(nameof(ProcessStatus));
                OnPropertyChanged(nameof(SnapshotSummary));
                OnPropertyChanged(nameof(AllySummary));
                OnPropertyChanged(nameof(EnemySummary));
                OnPropertyChanged(nameof(BattleTimerText));
                OnPropertyChanged(nameof(PlayerInfo));
                OnPropertyChanged(nameof(LastUpdatedText));
                UpdateEndpointHealth(value);
                UpdateBattleLog(value);
            }
        }
    }

    public bool IsOverlayEnabled
    {
        get => _isOverlayEnabled;
        set => SetProperty(ref _isOverlayEnabled, value);
    }

    public string Status => Ui.FormatStatus(Snapshot.Status);
    public string ProcessStatus => Snapshot.IsGameRunning ? Ui.AcesDetected : Ui.AcesNotDetected;
    public string SnapshotSummary => Ui.FormatObjectSummary(Snapshot.Objects.Count);
    public string AllySummary => Ui.FormatAllySummary(AllyCount);
    public string EnemySummary => Ui.FormatEnemySummary(EnemyCount);
    public string BattleTimerText => _sessionStartedAt.HasValue
        ? FormatElapsed(DateTimeOffset.Now - _sessionStartedAt.Value)
        : "0s";
    public string PlayerInfo => FormatPlayerInfo();
    public string LastUpdatedText => Snapshot.UpdatedAt == DateTimeOffset.MinValue
        ? Ui.NeverUpdated
        : Ui.FormatUpdated(Snapshot.UpdatedAt);

    private int AllyCount => Snapshot.Objects.Count(static o => o.IsAircraft && o.Kind is MapObjectKind.Ally or MapObjectKind.Squad or MapObjectKind.Player);
    private int EnemyCount => Snapshot.Objects.Count(static o => o.IsAircraft && o.Kind == MapObjectKind.Enemy);

    private void Start()
    {
        _service.Start(Settings.PollIntervalMs);
    }

    private void Stop()
    {
        _service.Stop();
    }

    private void Save()
    {
        _settingsStore.Save(Settings);
    }

    private void ResetSettings()
    {
        Settings.Map.Reset();
        Settings.Overlay.Reset();
        Save();
    }

    private void UpdateBattleLog(LiveSnapshot snapshot)
    {
        if (snapshot.UpdatedAt == DateTimeOffset.MinValue)
        {
            return;
        }

        _sessionStartedAt ??= DateTimeOffset.Now;
        var ally = AllyCount;
        var enemy = EnemyCount;
        foreach (var message in snapshot.Messages)
        {
            var key = $"{message.Enemy}:{message.Text}";
            if (_seenMessages.Add(key))
            {
                var prefix = message.Enemy switch
                {
                    true => Ui.MessageEnemy,
                    false => Ui.MessageAlly,
                    _ => Ui.MessageEvent
                };
                AddBattleLog($"{prefix}: {message.Text}");
            }
        }

        if (_lastAllyCount < 0 || _lastEnemyCount < 0)
        {
            AddBattleLog(Ui.FormatSessionStarted(ally, enemy));
        }
        else
        {
            if (ally != _lastAllyCount)
            {
                AddBattleLog(Ui.FormatAlliedAircraftChanged(_lastAllyCount, ally));
            }

            if (enemy != _lastEnemyCount)
            {
                AddBattleLog(Ui.FormatEnemyAircraftChanged(_lastEnemyCount, enemy));
            }
        }

        _lastAllyCount = ally;
        _lastEnemyCount = enemy;
    }

    private void AddBattleLog(string text)
    {
        BattleLogEntries.Add($"{DateTimeOffset.Now:HH:mm:ss}  {text}");
        while (BattleLogEntries.Count > 120)
        {
            BattleLogEntries.RemoveAt(0);
        }
    }

    private string FormatPlayerInfo()
    {
        var player = Snapshot.Player;
        if (player is null)
        {
            return Ui.PlayerNotDetected;
        }

        var parts = new List<string> { player.Name ?? Ui.Player };
        if (player.X.HasValue && player.Y.HasValue)
        {
            parts.Add($"{Ui.Pos} {player.X.Value:0.000}, {player.Y.Value:0.000}");
        }

        if (player.Mach.HasValue)
        {
            parts.Add($"M {player.Mach.Value:0.0}");
        }

        if (player.ClimbAngleDegrees.HasValue)
        {
            parts.Add($"{Ui.Climb} {player.ClimbAngleDegrees.Value:+0;-0;0} deg");
        }

        var speed = Snapshot.FlightState.TrueAirspeedKmh ?? Snapshot.FlightState.IndicatedAirspeedKmh;
        if (speed.HasValue)
        {
            parts.Add($"{speed.Value:0} km/h");
        }

        if (player.HeadingRadians.HasValue)
        {
            parts.Add($"{Ui.Heading} {player.HeadingRadians.Value * 180 / Math.PI:0} deg");
        }

        return string.Join(" | ", parts);
    }

    private static string FormatElapsed(TimeSpan elapsed)
    {
        return elapsed.TotalMinutes >= 1
            ? $"{(int)elapsed.TotalMinutes}m {elapsed.Seconds}s"
            : $"{Math.Max(0, elapsed.Seconds)}s";
    }

    private void OnSnapshotUpdated(object? sender, LiveSnapshot snapshot)
    {
        Dispatcher.UIThread.Post(() => Snapshot = snapshot);
    }

    private void RebuildBattleLog()
    {
        BattleLogEntries.Clear();
        _seenMessages.Clear();
        _lastAllyCount = -1;
        _lastEnemyCount = -1;
        UpdateBattleLog(Snapshot);
        UpdateEndpointHealth(Snapshot);
    }

    private void NotifyLocalizedProperties()
    {
        OnPropertyChanged(nameof(Status));
        OnPropertyChanged(nameof(ProcessStatus));
        OnPropertyChanged(nameof(SnapshotSummary));
        OnPropertyChanged(nameof(AllySummary));
        OnPropertyChanged(nameof(EnemySummary));
        OnPropertyChanged(nameof(BattleTimerText));
        OnPropertyChanged(nameof(PlayerInfo));
        OnPropertyChanged(nameof(LastUpdatedText));
        UpdateEndpointHealth(Snapshot);
    }

    private void UpdateEndpointHealth(LiveSnapshot snapshot)
    {
        EndpointHealthEntries.Clear();
        foreach (var health in snapshot.EndpointHealth)
        {
            EndpointHealthEntries.Add(Ui.FormatEndpointHealth(health));
        }
    }

    public void Dispose()
    {
        _service.SnapshotUpdated -= OnSnapshotUpdated;
        _settingsStore.Save(Settings);
        _service.Dispose();
    }
}
