using System;
using System.IO;
using System.IO.Compression;
using System.Text;
using System.Windows.Forms;

namespace ComputerShopWpfInstaller
{
    internal static class SetupStub
    {
        private const string AppName = "ComputerShopWpf";
        private const string DisplayName = "Продажа компьютерной техники";
        private static readonly byte[] Marker = Encoding.ASCII.GetBytes("CSWPF_SETUP_PAYLOAD_V1");

        [STAThread]
        private static int Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            try
            {
                string installDir = Path.Combine(
                    Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
                    AppName);

                DialogResult confirm = MessageBox.Show(
                    "Будет установлено приложение \"" + DisplayName + "\".\n\nПапка установки:\n" + installDir + "\n\nПродолжить?",
                    DisplayName + " - установщик",
                    MessageBoxButtons.YesNo,
                    MessageBoxIcon.Question);

                if (confirm != DialogResult.Yes)
                {
                    return 0;
                }

                byte[] payload = ReadPayload();
                ExtractPayload(payload, installDir);
                CreateDesktopShortcut(installDir);

                DialogResult launch = MessageBox.Show(
                    "Установка завершена.\n\nЯрлык создан на рабочем столе.\nЗапустить приложение сейчас?",
                    DisplayName + " - установщик",
                    MessageBoxButtons.YesNo,
                    MessageBoxIcon.Information);

                if (launch == DialogResult.Yes)
                {
                    System.Diagnostics.Process.Start(Path.Combine(installDir, "ComputerShopWpf.exe"));
                }

                return 0;
            }
            catch (Exception ex)
            {
                MessageBox.Show(
                    "Не удалось выполнить установку.\n\n" + ex.Message,
                    DisplayName + " - ошибка установки",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Error);
                return 1;
            }
        }

        private static byte[] ReadPayload()
        {
            byte[] exeBytes = File.ReadAllBytes(Application.ExecutablePath);
            int markerStart = exeBytes.Length - Marker.Length;

            if (markerStart <= 8 || !Matches(exeBytes, markerStart, Marker))
            {
                throw new InvalidOperationException("В установщике не найден пакет файлов приложения.");
            }

            long payloadLength = BitConverter.ToInt64(exeBytes, markerStart - 8);
            if (payloadLength <= 0 || payloadLength > markerStart - 8)
            {
                throw new InvalidOperationException("Пакет файлов приложения поврежден.");
            }

            int payloadStart = markerStart - 8 - (int)payloadLength;
            byte[] payload = new byte[payloadLength];
            Buffer.BlockCopy(exeBytes, payloadStart, payload, 0, payload.Length);
            return payload;
        }

        private static bool Matches(byte[] data, int start, byte[] pattern)
        {
            for (int i = 0; i < pattern.Length; i++)
            {
                if (data[start + i] != pattern[i])
                {
                    return false;
                }
            }

            return true;
        }

        private static void ExtractPayload(byte[] payload, string installDir)
        {
            Directory.CreateDirectory(installDir);
            string fullInstallDir = Path.GetFullPath(installDir);

            using (MemoryStream stream = new MemoryStream(payload))
            using (ZipArchive archive = new ZipArchive(stream, ZipArchiveMode.Read))
            {
                foreach (ZipArchiveEntry entry in archive.Entries)
                {
                    string targetPath = Path.GetFullPath(Path.Combine(installDir, entry.FullName));
                    if (!targetPath.StartsWith(fullInstallDir, StringComparison.OrdinalIgnoreCase))
                    {
                        throw new InvalidOperationException("Пакет содержит недопустимый путь: " + entry.FullName);
                    }

                    if (entry.FullName.EndsWith("/", StringComparison.Ordinal))
                    {
                        Directory.CreateDirectory(targetPath);
                        continue;
                    }

                    string directory = Path.GetDirectoryName(targetPath);
                    if (!String.IsNullOrEmpty(directory))
                    {
                        Directory.CreateDirectory(directory);
                    }

                    entry.ExtractToFile(targetPath, true);
                }
            }
        }

        private static void CreateDesktopShortcut(string installDir)
        {
            string exePath = Path.Combine(installDir, "ComputerShopWpf.exe");
            string desktop = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
            CreateShortcut(
                Path.Combine(desktop, DisplayName + ".lnk"),
                exePath,
                installDir,
                DisplayName,
                exePath);

            string uninstallScript = Path.Combine(installDir, "uninstall.ps1");
            if (File.Exists(uninstallScript))
            {
                CreateShortcut(
                    Path.Combine(desktop, DisplayName + " - удалить.lnk"),
                    "powershell.exe",
                    installDir,
                    "Удалить " + DisplayName,
                    "powershell.exe",
                    "-NoProfile -ExecutionPolicy Bypass -File \"" + uninstallScript + "\"");
            }
        }

        private static void CreateShortcut(
            string shortcutPath,
            string targetPath,
            string workingDirectory,
            string description,
            string iconPath,
            string arguments = "")
        {
            Type shellType = Type.GetTypeFromProgID("WScript.Shell");
            if (shellType == null)
            {
                throw new InvalidOperationException("Не удалось создать ярлык: WScript.Shell недоступен.");
            }

            object shell = Activator.CreateInstance(shellType);
            object shortcut = shellType.InvokeMember(
                "CreateShortcut",
                System.Reflection.BindingFlags.InvokeMethod,
                null,
                shell,
                new object[] { shortcutPath });

            Type shortcutType = shortcut.GetType();
            shortcutType.InvokeMember("TargetPath", System.Reflection.BindingFlags.SetProperty, null, shortcut, new object[] { targetPath });
            shortcutType.InvokeMember("WorkingDirectory", System.Reflection.BindingFlags.SetProperty, null, shortcut, new object[] { workingDirectory });
            shortcutType.InvokeMember("Description", System.Reflection.BindingFlags.SetProperty, null, shortcut, new object[] { description });
            shortcutType.InvokeMember("IconLocation", System.Reflection.BindingFlags.SetProperty, null, shortcut, new object[] { iconPath });

            if (!String.IsNullOrEmpty(arguments))
            {
                shortcutType.InvokeMember("Arguments", System.Reflection.BindingFlags.SetProperty, null, shortcut, new object[] { arguments });
            }

            shortcutType.InvokeMember("Save", System.Reflection.BindingFlags.InvokeMethod, null, shortcut, null);
        }
    }
}
