using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Deployment.Application;
using System.Diagnostics;
using System.Drawing;
using Newtonsoft.Json;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net.Http;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
using System.Reflection.Emit;

namespace ProgramLauncher
{
    public partial class Form1 : Form
    {
        static string downloadServerUrl = "http://xr.flexing.ai:3030/XR_Server";
        static string downloadFolderName = "Build";
        static string jsonFileName = "file_list.json";
        static string localVersionID = "versionID.txt";

        long totalBytesToDownload = 0;
        long totalBytesDownloaded = 0;

        static readonly HttpClient httpClient = new HttpClient();

        public Form1()
        {
            downloadServerUrl = CombineUrl(downloadServerUrl, Program.projectName);
            InitializeComponent();
        }

        private async void Form1_Load(object sender, EventArgs e)
        {
            Label_UpdateFileName.Text = string.Empty;

            await CheckUpdate();
        }

        private void WriteText(string text)
        {
            Label_UpdateFileName.Text = text;
        }

        private async Task CheckUpdate()
        {
            var serverFileList = await GetServerFileList(downloadServerUrl);
            if (serverFileList == null) return;

            string latestVersionID = serverFileList.VersionID;
            string localVersionID = GetLocalVersion();

            if (latestVersionID != localVersionID)
            {
                // ✅ [1] 전체 크기 계산
                foreach (var file in serverFileList.Files)
                {
                    string fileUrl = CombineUrl(downloadServerUrl, Path.Combine(downloadFolderName, file.Key));

                    using (var response = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, fileUrl)))
                    {
                        if (response.IsSuccessStatusCode && response.Content.Headers.ContentLength.HasValue)
                        {
                            totalBytesToDownload += response.Content.Headers.ContentLength.Value;
                        }
                    }
                }

                // ✅ [2] ProgressBar 초기화
                ProgressBar.Invoke((MethodInvoker)(() =>
                {
                    ProgressBar.Minimum = 0;
                    ProgressBar.Maximum = (int)Math.Max(totalBytesToDownload, 1); // 0 방지
                    ProgressBar.Value = 0;
                }));


                foreach (var file in serverFileList.Files)
                {
                    string localFilePath = Path.Combine(downloadFolderName, file.Key);
                    string serverHash = file.Value;

                    
                    if (!File.Exists(localFilePath) || GetFileHash(localFilePath) != serverHash)
                    {
                        Label_UpdateFileName.Text = localFilePath;
                        string downloadFolder = CombineUrl(downloadServerUrl, downloadFolderName);
                        string downloadFile = Path.Combine(downloadFolder, file.Key);

                        await DownloadFile(downloadFile, localFilePath, (downloadedBytes) =>
                        {
                            totalBytesDownloaded += downloadedBytes;
                            ProgressBar.Invoke((MethodInvoker)(() =>
                            {
                                ProgressBar.Value = (int)Math.Min(totalBytesDownloaded, ProgressBar.Maximum);
                            }));
                        });
                    }
                }

                var serverFilePaths = new HashSet<string>(
                serverFileList.Files.Keys.Select(file => file.Replace('\\', '/')) // 서버의 파일 경로를 통일
                );

                foreach (string localFile in Directory.GetFiles(downloadFolderName, "*", SearchOption.AllDirectories))
                {
                    // 현재 파일의 상대 경로를 구함 (localFolder 기준)
                    string relativePath = localFile.Substring(downloadFolderName.Length + 1).Replace('\\', '/');

                    if (!serverFilePaths.Contains(relativePath))
                    {
                        File.Delete(localFile);
                    }
                }

                SaveLocalVersion(latestVersionID);
            }

            StartExecutableFile(downloadFolderName);
            this.Close();
        }

        private async Task<ServerFileList> GetServerFileList(string serverUrl)
        {
            using (WebClient client = new WebClient())
            {
                try
                {
                    string serverJsonUrl = CombineUrl(serverUrl, jsonFileName);
                    string json = await client.DownloadStringTaskAsync(serverJsonUrl);
                    return JsonConvert.DeserializeObject<ServerFileList>(json);
                }
                catch (Exception ex)
                {
                    return null;
                }
            }
        }

        private string GetLocalVersion()
        {
            return File.Exists(localVersionID) ? File.ReadAllText(localVersionID).Trim() : "";
        }
        private void SaveLocalVersion(string version)
        {
            File.WriteAllText(localVersionID, version);
        }

        private string GetFileHash(string filePath)
        {
            using (var sha256 = SHA256.Create())
            using (var stream = File.OpenRead(filePath))
            {
                byte[] hash = sha256.ComputeHash(stream);
                return BitConverter.ToString(hash).Replace("-", "").ToLower();
            }
        }

        private async Task DownloadFile(string url, string outputPath, Action<long> reportProgress)
        {
            try
            {
                Directory.CreateDirectory(Path.GetDirectoryName(outputPath) ?? "");

                using (var response = await httpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead))
                {
                    response.EnsureSuccessStatusCode();

                    using (var stream = await response.Content.ReadAsStreamAsync())
                    using (var fileStream = new FileStream(outputPath, FileMode.Create, FileAccess.Write, FileShare.None))
                    {
                        byte[] buffer = new byte[81920];
                        int bytesRead;
                        while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length)) > 0)
                        {
                            await fileStream.WriteAsync(buffer, 0, bytesRead);
                            reportProgress(bytesRead); // 매번 다운로드된 바이트 보고
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                WriteText($"❌ 다운로드 실패: {ex.Message}");
            }
        }

        private void StartExecutableFile(string executeFolder)
        {
            string[] exeFiles = Directory.GetFiles(executeFolder, "*.exe", SearchOption.TopDirectoryOnly);

            if (exeFiles.Length == 0)
            {
                return;
            }

            string executeFilePath = exeFiles.FirstOrDefault(file => !file.EndsWith("UnityCrashHandler64.exe", StringComparison.OrdinalIgnoreCase));
            string executeFileName = Path.GetFileName(executeFilePath);

            Process process = new Process();
            process.StartInfo.UseShellExecute = true;
            process.StartInfo.WorkingDirectory = executeFolder;
            process.StartInfo.FileName = executeFileName;
            process.Start();
        }

        private string CombineUrl(string baseUrl, string relativePath)
        {
            return $"{baseUrl.TrimEnd('/')}/{relativePath.TrimStart('/')}";
        }
    }

    class ServerFileList
    {
        public string VersionID { get; set; } = "";
        public Dictionary<string, string> Files { get; set; } = new Dictionary<string, string>();
    }
}

