using System.Collections.ObjectModel;
using System.IO;
using AppTunnel.Models;
using AppTunnel.Services;
using AppTunnel.Views;

namespace AppTunnel.ViewModels;

public partial class MainViewModel
{
    #region Profile Management

    public ObservableCollection<ConnectionProfile> Profiles { get; } = new();

    private ConnectionProfile? _selectedProfile;
    public ConnectionProfile? SelectedProfile
    {
        get => _selectedProfile;
        set
        {
            if (_selectedProfile == value) return;
            _saveDebounceTimer.Stop();
            SaveCurrentProfileState();
            _selectedProfile = value;
            OnPropertyChanged();
            OnPropertyChanged(nameof(SelectedProfileName));
            RaiseProfileCardChanged();
            if (value != null)
                LoadProfileIntoUi(value);
            SaveProfiles();
        }
    }

    public string SelectedProfileName => _selectedProfile?.Name ?? "";
    public string ProfileCountText => Profiles.Count == 1
        ? "۱ پروفایل ذخیره‌شده"
        : $"{Profiles.Count} پروفایل ذخیره‌شده";

    public string ActiveProfileTypeText => CurrentTunnelType switch
    {
        TunnelType.L2tpIpsec => "L2TP/IPsec",
        TunnelType.V2Ray => TunnelProviderFactory.RequiresXray(SelectedV2RayConfig) ? "V2Ray / Xray" : "V2Ray / sing-box",
        TunnelType.OpenVpn => "OpenVPN",
        TunnelType.SocksProxy => ProxyProtocol == ProxyProtocol.Http ? "HTTP Proxy" : "SOCKS5 Proxy",
        _ => "نوع اتصال نامشخص"
    };

    public string ActiveProfileEndpointText => CurrentTunnelType switch
    {
        TunnelType.L2tpIpsec => string.IsNullOrWhiteSpace(ServerAddress) ? "آدرس سرور هنوز وارد نشده" : ServerAddress.Trim(),
        TunnelType.V2Ray => TryExtractProxyEndpoint(SelectedV2RayConfig.Trim(), out var server, out var port, out _)
            ? $"{server}:{port}"
            : "کانفیگ V2Ray/Xray آماده نمایش نیست",
        TunnelType.OpenVpn => string.IsNullOrWhiteSpace(SelectedOpenVpnConfigPath)
            ? "فایل OpenVPN انتخاب نشده"
            : Path.GetFileName(SelectedOpenVpnConfigPath),
        TunnelType.SocksProxy => string.IsNullOrWhiteSpace(ProxyServerAddress)
            ? "آدرس پراکسی هنوز وارد نشده"
            : $"{ProxyServerAddress.Trim()}:{ProxyPort}",
        _ => ""
    };

    public string ProfileSaveHintText => string.IsNullOrWhiteSpace(SaveStatusText)
        ? "تغییرات این پروفایل به‌صورت خودکار ذخیره می‌شود"
        : SaveStatusText;

    /// <summary>
    /// Event to notify code-behind to update PasswordBox controls.
    /// </summary>
    public event Action<string, string>? PasswordChanged;
    public event Action<string>? OpenVpnPasswordChanged;
    public event Action<string>? ProxyPasswordChanged;

    private void LoadProfiles()
    {
        var profiles = _profileService.LoadProfiles();
        Profiles.Clear();

        if (profiles.Count == 0)
            profiles.Add(new ConnectionProfile { Name = "پیش‌فرض" });

        foreach (var p in profiles.OrderByDescending(p => p.LastUsedAt))
            Profiles.Add(p);
        OnPropertyChanged(nameof(ProfileCountText));

        _selectedProfile = Profiles[0];
        OnPropertyChanged(nameof(SelectedProfile));
        OnPropertyChanged(nameof(SelectedProfileName));
        LoadProfileIntoUi(Profiles[0]);
    }

    private void SaveProfiles() => _profileService.SaveProfiles(Profiles);

    private void LoadExcludes()
    {
        var excludes = _profileService.LoadExcludes();
        ExcludedDestinations.Clear();
        foreach (var e in excludes)
            ExcludedDestinations.Add(e);
    }

    private void SaveExcludes() => _profileService.SaveExcludes(ExcludedDestinations);

    private void LoadIncludes()
    {
        var includes = _profileService.LoadIncludes();
        IncludedDestinations.Clear();
        foreach (var i in includes)
            IncludedDestinations.Add(i);
    }

    private void SaveIncludes() => _profileService.SaveIncludes(IncludedDestinations);

    private void LoadTunnelApps()
    {
        var apps = _profileService.LoadTunnelApps();
        TunnelApps.Clear();
        foreach (var app in apps)
        {
            var icon = AppDiscoveryService.ExtractIcon(app.ExecutablePath);
            TunnelApps.Add(new AppItemViewModel(new TunnelApp
            {
                DisplayName = app.DisplayName,
                ExecutablePath = app.ExecutablePath,
                ExecutableName = app.ExecutableName,
                Icon = icon,
                IsEnabled = app.IsEnabled
            }) { IsEnabled = app.IsEnabled });
        }
        RefreshAllFilters();
        OnPropertyChanged(nameof(EnabledAppsCount));
    }

    private void SaveTunnelApps() => _profileService.SaveTunnelApps(
        TunnelApps.Select(a => new ProfileApp
        {
            DisplayName = a.DisplayName,
            ExecutablePath = a.ExecutablePath,
            ExecutableName = a.ExecutableName,
            IsEnabled = a.IsEnabled
        }));

    private void SaveCurrentProfileState()
    {
        if (_selectedProfile == null) return;
        _selectedProfile.ServerAddress = ServerAddress;
        _selectedProfile.Username = Username;
        _selectedProfile.Password = Password;
        _selectedProfile.PreSharedKey = PreSharedKey;
        _selectedProfile.TunnelType = _currentTunnelType;
        _selectedProfile.V2RayConfig = SelectedV2RayConfig;
        _selectedProfile.OpenVpnConfig = SelectedOpenVpnConfig;
        _selectedProfile.OpenVpnConfigPath = SelectedOpenVpnConfigPath;
        _selectedProfile.OpenVpnUsername = OpenVpnUsername;
        _selectedProfile.OpenVpnPassword = OpenVpnPassword;
        _selectedProfile.ProxyProtocol = ProxyProtocol;
        _selectedProfile.ProxyServerAddress = ProxyServerAddress;
        _selectedProfile.ProxyPort = ProxyPort;
        _selectedProfile.ProxyUsername = ProxyUsername;
        _selectedProfile.ProxyPassword = ProxyPassword;
        _selectedProfile.MixedProxyPort = MixedProxyPort;
        _selectedProfile.AutoTuneMtu = AutoTuneMtu;
        _selectedProfile.EnableDnsOptimization = IsDnsOptimizationEnabled;
        _selectedProfile.EnableGameMode = IsGameModeEnabled;
    }

    /// <summary>
    /// Called from code-behind after PasswordBox changes to persist state.
    /// Uses debounce to avoid saving on every keystroke.
    /// </summary>
    public void SaveCurrentState()
    {
        if (_isLoadingProfile) return;
        SaveStatusText = "در حال ذخیره...";
        _saveDebounceTimer.Stop();
        _saveDebounceTimer.Start(); // Restart timer - will save after 1 second of no changes
    }

    /// <summary>
    /// Force immediate save without debounce (for app shutdown).
    /// </summary>
    public void ForceSave()
    {
        _saveDebounceTimer.Stop();
        SaveCurrentProfileState();
        SaveProfiles();
        SaveStatusText = "ذخیره شد";
    }

    private void LoadProfileIntoUi(ConnectionProfile profile)
    {
        _isLoadingProfile = true;
        try
        {
            ServerAddress = profile.ServerAddress;
            Username = profile.Username;
            Password = profile.Password;
            PreSharedKey = profile.PreSharedKey;
            _mixedProxyPort = profile.MixedProxyPort > 0 ? profile.MixedProxyPort : 1080;
            _trafficRouter.Socks5Port = _mixedProxyPort;
            OnPropertyChanged(nameof(MixedProxyPort));
            OnPropertyChanged(nameof(MixedProxyPortText));
            OnPropertyChanged(nameof(MixedProxyInfo));
            UpdateMixedProxyPortStatus();
            _autoTuneMtu = profile.AutoTuneMtu;
            _isDnsOptimizationEnabled = profile.EnableDnsOptimization;
            _isGameModeEnabled = profile.EnableGameMode;
            _trafficRouter.EnableDnsOptimization = _isDnsOptimizationEnabled;
            _trafficRouter.EnableGameMode = _isGameModeEnabled;
            OnPropertyChanged(nameof(AutoTuneMtu));
            OnPropertyChanged(nameof(IsDnsOptimizationEnabled));
            OnPropertyChanged(nameof(IsGameModeEnabled));
            OnPropertyChanged(nameof(GameModeStatusText));
            // Use the field directly to avoid writing back to the old profile
            // while the new profile is being loaded.
            _currentTunnelType = profile.TunnelType;
            _selectedV2RayConfig = profile.V2RayConfig;
            _selectedOpenVpnConfig = profile.OpenVpnConfig;
            _selectedOpenVpnConfigPath = profile.OpenVpnConfigPath;
            _openVpnUsername = profile.OpenVpnUsername;
            _openVpnPassword = profile.OpenVpnPassword;
            _proxyProtocol = profile.ProxyProtocol;
            _proxyServerAddress = profile.ProxyServerAddress;
            _proxyPort = profile.ProxyPort > 0 ? profile.ProxyPort : 1080;
            _proxyUsername = profile.ProxyUsername;
            _proxyPassword = profile.ProxyPassword;
            OnPropertyChanged(nameof(CurrentTunnelType));
            OnPropertyChanged(nameof(ConnectedBadgeText));
            OnPropertyChanged(nameof(SelectedV2RayConfig));
            OnPropertyChanged(nameof(SelectedOpenVpnConfig));
            OnPropertyChanged(nameof(SelectedOpenVpnConfigPath));
            OnPropertyChanged(nameof(OpenVpnUsername));
            OnPropertyChanged(nameof(ProxyProtocol));
            OnPropertyChanged(nameof(ProxyServerAddress));
            OnPropertyChanged(nameof(ProxyPort));
            OnPropertyChanged(nameof(ProxyPortText));
            OnPropertyChanged(nameof(ProxyUsername));
            UpdateConfigDiagnostics();
            RaiseProfileCardChanged();

            PasswordChanged?.Invoke(profile.Password, profile.PreSharedKey);
            OpenVpnPasswordChanged?.Invoke(profile.OpenVpnPassword);
            ProxyPasswordChanged?.Invoke(profile.ProxyPassword);
        }
        finally
        {
            _isLoadingProfile = false;
        }
    }

    private void CreateNewProfile()
    {
        SaveCurrentProfileState();
        var profile = new ConnectionProfile
        {
            Name = $"پروفایل {Profiles.Count + 1}",
            MixedProxyPort = MixedProxyPort,
            AutoTuneMtu = AutoTuneMtu,
            EnableDnsOptimization = IsDnsOptimizationEnabled,
            EnableGameMode = IsGameModeEnabled
        };

        if (ProfileEditorDialog.Show(profile, "افزودن کانفیگ جدید", System.Windows.Application.Current.MainWindow) != true)
            return;

        Profiles.Add(profile);
        OnPropertyChanged(nameof(ProfileCountText));
        SelectedProfile = profile;
        SaveProfiles();
    }

    private void DuplicateCurrentProfile(object? parameter = null)
    {
        var source = parameter as ConnectionProfile ?? _selectedProfile;
        if (source == null) return;
        SaveCurrentProfileState();

        var clone = CloneProfile(source);
        clone.Name = $"{source.Name} (کپی)";

        if (ProfileEditorDialog.Show(clone, "کپی پروفایل", System.Windows.Application.Current.MainWindow) != true)
            return;

        Profiles.Add(clone);
        OnPropertyChanged(nameof(ProfileCountText));
        SelectedProfile = clone;
        SaveProfiles();
    }

    private void EditProfile(object? parameter)
    {
        var profile = parameter as ConnectionProfile ?? _selectedProfile;
        if (profile == null) return;

        SaveCurrentProfileState();
        var editable = CloneProfile(profile);
        editable.Id = profile.Id;
        editable.CreatedAt = profile.CreatedAt;
        editable.LastUsedAt = profile.LastUsedAt;

        if (ProfileEditorDialog.Show(editable, "ویرایش پروفایل", System.Windows.Application.Current.MainWindow) != true)
            return;

        ApplyProfileValues(profile, editable);
        if (_selectedProfile == profile)
            LoadProfileIntoUi(profile);
        SaveProfiles();
        RaiseProfileCardChanged();
    }

    private void SelectProfile(object? parameter)
    {
        if (parameter is ConnectionProfile profile)
            SelectedProfile = profile;
    }

    private void DeleteCurrentProfile(object? parameter = null)
    {
        var toRemove = parameter as ConnectionProfile ?? _selectedProfile;
        if (toRemove == null || Profiles.Count <= 1) return;
        if (!Helpers.DialogService.Confirm($"پروفایل «{toRemove.Name}» حذف شود؟", "حذف پروفایل"))
            return;

        var idx = Profiles.IndexOf(toRemove);
        Profiles.Remove(toRemove);
        OnPropertyChanged(nameof(ProfileCountText));
        SelectedProfile = Profiles[Math.Min(idx, Profiles.Count - 1)];
        SaveProfiles();
    }

    private static ConnectionProfile CloneProfile(ConnectionProfile source) => new()
    {
        Name = source.Name,
        ServerAddress = source.ServerAddress,
        Username = source.Username,
        Password = source.Password,
        PreSharedKey = source.PreSharedKey,
        TunnelType = source.TunnelType,
        V2RayConfig = source.V2RayConfig,
        OpenVpnConfig = source.OpenVpnConfig,
        OpenVpnConfigPath = source.OpenVpnConfigPath,
        OpenVpnUsername = source.OpenVpnUsername,
        OpenVpnPassword = source.OpenVpnPassword,
        ProxyProtocol = source.ProxyProtocol,
        ProxyServerAddress = source.ProxyServerAddress,
        ProxyPort = source.ProxyPort,
        ProxyUsername = source.ProxyUsername,
        ProxyPassword = source.ProxyPassword,
        MixedProxyPort = source.MixedProxyPort,
        AutoTuneMtu = source.AutoTuneMtu,
        EnableDnsOptimization = source.EnableDnsOptimization,
        EnableGameMode = source.EnableGameMode
    };

    private static void ApplyProfileValues(ConnectionProfile target, ConnectionProfile source)
    {
        target.Name = source.Name;
        target.ServerAddress = source.ServerAddress;
        target.Username = source.Username;
        target.Password = source.Password;
        target.PreSharedKey = source.PreSharedKey;
        target.TunnelType = source.TunnelType;
        target.V2RayConfig = source.V2RayConfig;
        target.OpenVpnConfig = source.OpenVpnConfig;
        target.OpenVpnConfigPath = source.OpenVpnConfigPath;
        target.OpenVpnUsername = source.OpenVpnUsername;
        target.OpenVpnPassword = source.OpenVpnPassword;
        target.ProxyProtocol = source.ProxyProtocol;
        target.ProxyServerAddress = source.ProxyServerAddress;
        target.ProxyPort = source.ProxyPort;
        target.ProxyUsername = source.ProxyUsername;
        target.ProxyPassword = source.ProxyPassword;
        target.MixedProxyPort = source.MixedProxyPort;
        target.AutoTuneMtu = source.AutoTuneMtu;
        target.EnableDnsOptimization = source.EnableDnsOptimization;
        target.EnableGameMode = source.EnableGameMode;
    }

    private void RaiseProfileCardChanged()
    {
        OnPropertyChanged(nameof(ProfileCountText));
        OnPropertyChanged(nameof(ActiveProfileTypeText));
        OnPropertyChanged(nameof(ActiveProfileEndpointText));
        OnPropertyChanged(nameof(ProfileSaveHintText));
    }

    #endregion

    #region History

    private void LoadHistory()
    {
        var entries = _historyService.LoadHistory();
        ConnectionHistory.Clear();
        foreach (var entry in entries)
            ConnectionHistory.Add(entry);
        OnPropertyChanged(nameof(TotalHistoryData));
    }

    private void ClearHistory()
    {
        _historyService.ClearHistory();
        ConnectionHistory.Clear();
        OnPropertyChanged(nameof(TotalHistoryData));
    }

    private void SaveConnectionToHistory()
    {
        if (_connectionStartTime == default) return;

        // Use authoritative VPN-interface counters for the total,
        // not the sum of per-app counters (which may miss tail packets
        // and non-attributed traffic like VPN keepalives).
        var (totalSent, totalReceived) = _trafficRouter.GetTotalVpnTraffic();

        var entry = new ConnectionHistoryEntry
        {
            ProfileName = _selectedProfile?.Name ?? "پیش‌فرض",
            ServerAddress = CurrentTunnelType == TunnelType.SocksProxy
                ? $"{ProxyServerAddress}:{ProxyPort}"
                : ServerAddress,
            ConnectedAt = _connectionStartTime,
            DisconnectedAt = DateTime.Now,
            BytesSent = totalSent,
            BytesReceived = totalReceived
        };

        _historyService.AddEntry(entry);
        ConnectionHistory.Insert(0, entry);
        _connectionStartTime = default;
        OnPropertyChanged(nameof(TotalHistoryData));
    }

    #endregion
}
