using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using PolyTraderSharp.Models;

namespace PolyTraderSharp.Services
{
    public class MullvadVpnService : BackgroundService
    {
        private readonly TerminalLogger _logger;
        private ServerSettings _settings;
        private readonly string _settingsPath = "server_settings.xml";
        private bool _isConnected = false;
        private int _consecutiveFailures = 0;
        private readonly int _maxRetries = 3;

        public bool IsConnected => _isConnected;

        public MullvadVpnService(TerminalLogger logger)
        {
            _logger = logger;
            _settings = ServerSettings.Load(_settingsPath);
        }

        public void ReloadSettings()
        {
            _settings = ServerSettings.Load(_settingsPath);
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                if (_settings.VpnEnabled)
                {
                    await HealthCheckAsync();
                }
                await Task.Delay(TimeSpan.FromSeconds(30), stoppingToken);
            }
        }

        public async Task<bool> HealthCheckAsync()
        {
            if (!_settings.VpnEnabled) return true;

            string status = await RunCliAsync("status");
            if (status.Contains("Connected"))
            {
                _isConnected = true;
                _consecutiveFailures = 0;
                return true;
            }

            // Try reconnecting
            _logger.Warning("VPN is not connected. Attempting to reconnect...");
            if (await ConnectAsync()) return true;

            _consecutiveFailures++;
            if (_consecutiveFailures >= _maxRetries)
            {
                _logger.Error($"VPN unrecoverable after {_maxRetries} retries.");
            }
            return false;
        }

        public async Task<bool> ConnectAsync()
        {
            if (!_settings.VpnEnabled) return true;

            string status = await RunCliAsync("status");
            if (status.Contains("Connected"))
            {
                if (string.IsNullOrEmpty(_settings.VpnLocation) || status.Contains(_settings.VpnLocation, StringComparison.OrdinalIgnoreCase))
                {
                    _logger.Info($"VPN already connected tightly to {_settings.VpnLocation}");
                    _isConnected = true;
                    _consecutiveFailures = 0;
                    return true;
                }
            }

            if (!string.IsNullOrEmpty(_settings.MullvadAccount))
            {
                await RunCliAsync($"account login {_settings.MullvadAccount}");
                await Task.Delay(1000);
            }

            await RunCliAsync("lan set allow");

            if (!string.IsNullOrEmpty(_settings.VpnLocation))
            {
                await RunCliAsync($"relay set location {_settings.VpnLocation}");
                await Task.Delay(1000);
            }

            await RunCliAsync("connect");

            for (int i = 0; i < 10; i++)
            {
                await Task.Delay(2000);
                string check = await RunCliAsync("status");
                if (check.Contains("Connected"))
                {
                    _isConnected = true;
                    _consecutiveFailures = 0;
                    _logger.Info($"VPN connected successfully: {check.Trim()}");
                    return true;
                }
            }

            _logger.Error("VPN failed to connect after waiting");
            _consecutiveFailures++;
            return false;
        }

        public async Task<bool> DisconnectAsync()
        {
            string result = await RunCliAsync("disconnect");
            _isConnected = false;
            _logger.Info("VPN disconnected.");
            return true;
        }

        private async Task<string> RunCliAsync(string args)
        {
            try
            {
                if (!File.Exists(_settings.MullvadCliPath))
                {
                    _logger.Error($"Mullvad CLI not found at: {_settings.MullvadCliPath}");
                    return string.Empty;
                }

                var psi = new ProcessStartInfo
                {
                    FileName = _settings.MullvadCliPath,
                    Arguments = args,
                    RedirectStandardOutput = true,
                    RedirectStandardError = true,
                    UseShellExecute = false,
                    CreateNoWindow = true
                };

                using var process = Process.Start(psi);
                if (process == null) return string.Empty;

                await process.WaitForExitAsync();
                string output = await process.StandardOutput.ReadToEndAsync();
                string err = await process.StandardError.ReadToEndAsync();
                return string.IsNullOrWhiteSpace(output) ? err : output;
            }
            catch (Exception ex)
            {
                _logger.Error($"Mullvad CLI exc: {ex.Message}");
                return string.Empty;
            }
        }
    }
}
