using System;
using System.IO;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Saradomin.Model.Settings.Launcher;
using Saradomin.Utilities;

namespace Saradomin.Infrastructure.Services
{
    public class ClientUpdateService : IClientUpdateService
    {
        private readonly ISettingsService _settingsService;

        private float CurrentDownloadProgress { get; set; }

        public string ClientDownloadURL => "https://assets.emoscape.org/rt4-client.jar";
        public string ClientHashURL => "https://assets.emoscape.org/rt4-client.jar.sha256";

        public string PreferredTargetFilePath =>
            CrossPlatform.Get2009scapeExecutable();

        public event EventHandler<float> DownloadProgressChanged;

        public ClientUpdateService(ISettingsService settingsService)
        {
            _settingsService = settingsService;
        }

        public async Task<string> FetchRemoteClientHashAsync(CancellationToken cancellationToken)
        {
            using var httpClient = new HttpClient();
            {
                var response = await httpClient.GetAsync(ClientHashURL, cancellationToken);
                return await response.Content.ReadAsStringAsync(cancellationToken);
            }
        }

        public async Task FetchRemoteClientExecutableAsync(CancellationToken cancellationToken, string targetPath = null)
        {
            CurrentDownloadProgress = 0;
            targetPath ??= PreferredTargetFilePath;
    
            // Define a temporary file path to download to
            string tempFilePath = targetPath + ".tmp";

            // Download the client to the temporary file regardless of whether it already exists.
            using (var httpClient = new HttpClient())
            {
                var response = await httpClient.GetAsync(ClientDownloadURL, cancellationToken);
                var contentLength = response.Content.Headers.ContentLength ?? 12 * 1024 * 1024 * 1024f;

                using var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken);
                using (var outFileStream = File.Create(tempFilePath))
                {
                    var buffer = new byte[1024];
                    var totalRead = 0;
                    int bytesRead;
                    while ((bytesRead = await responseStream.ReadAsync(buffer, 0, buffer.Length, cancellationToken)) > 0)
                    {
                        await outFileStream.WriteAsync(buffer, 0, bytesRead, cancellationToken);
                        totalRead += bytesRead;
                        CurrentDownloadProgress = totalRead / contentLength;
                        DownloadProgressChanged?.Invoke(this, CurrentDownloadProgress);
                    }
                }
            }

            // Replace the existing file only after a successful download.
            if (File.Exists(targetPath))
            {
                File.Delete(targetPath);
            }
            File.Move(tempFilePath, targetPath);
        }


        public async Task<string> ComputeLocalClientHashAsync(string filePath = null)
        {
            filePath ??= PreferredTargetFilePath;

            if (!File.Exists(filePath))
                throw new FileNotFoundException($"Unable to calculate local client hash. File '{filePath}' missing.");

            await using var stream = File.OpenRead(filePath);
            {
                var sha256 = SHA256.Create();
                stream.Position = 0;
                var hash = await sha256.ComputeHashAsync(stream);
                return BitConverter.ToString(hash).Replace("-", string.Empty);
            }
        }
    }
}