using System.IO.Compression;
using System.Reflection;
using System.Runtime.InteropServices;

namespace AutorentSetup;

internal static class Program
{
    [STAThread]
    private static void Main()
    {
        ApplicationConfiguration.Initialize();
        Application.Run(new SetupForm());
    }
}

public sealed class SetupForm : Form
{
    private readonly TextBox _folderBox = new()
    {
        Text = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "AutorentWinForms"),
        Width = 430
    };

    private readonly CheckBox _desktopShortcutBox = new()
    {
        Text = "Создать ярлык на рабочем столе",
        Checked = true,
        AutoSize = true
    };

    private readonly Button _installButton = new() { Text = "Установить", Width = 120, Height = 34 };
    private readonly Label _statusLabel = new() { AutoSize = true, Text = "Готово к установке." };
    private readonly ProgressBar _progress = new() { Width = 570, Height = 18 };

    public SetupForm()
    {
        Text = "Установка Автопрокат";
        StartPosition = FormStartPosition.CenterScreen;
        FormBorderStyle = FormBorderStyle.FixedDialog;
        MaximizeBox = false;
        MinimizeBox = false;
        ClientSize = new Size(640, 330);
        Font = new Font("Segoe UI", 10F);

        var title = new Label
        {
            Text = "Мастер установки ИС «Автопрокат»",
            Font = new Font("Segoe UI", 15F, FontStyle.Bold),
            AutoSize = true,
            Location = new Point(28, 24)
        };

        var description = new Label
        {
            Text = "Программа установит приложение, файл настроек БД и необходимые зависимости.",
            AutoSize = true,
            Location = new Point(31, 62)
        };

        var folderLabel = new Label
        {
            Text = "Папка установки:",
            AutoSize = true,
            Location = new Point(31, 105)
        };

        _folderBox.Location = new Point(34, 132);

        var browseButton = new Button
        {
            Text = "Обзор...",
            Width = 100,
            Height = 30,
            Location = new Point(475, 130)
        };
        browseButton.Click += (_, _) => BrowseFolder();

        _desktopShortcutBox.Location = new Point(34, 178);
        _progress.Location = new Point(34, 222);
        _statusLabel.Location = new Point(34, 250);

        _installButton.Location = new Point(365, 282);
        _installButton.Click += InstallButton_Click;

        var closeButton = new Button
        {
            Text = "Отмена",
            Width = 100,
            Height = 34,
            Location = new Point(475, 282)
        };
        closeButton.Click += (_, _) => Close();

        Controls.AddRange([
            title,
            description,
            folderLabel,
            _folderBox,
            browseButton,
            _desktopShortcutBox,
            _progress,
            _statusLabel,
            _installButton,
            closeButton
        ]);
    }

    private void BrowseFolder()
    {
        using var dialog = new FolderBrowserDialog
        {
            Description = "Выберите папку для установки Автопроката",
            UseDescriptionForTitle = true,
            SelectedPath = _folderBox.Text
        };

        if (dialog.ShowDialog(this) == DialogResult.OK)
            _folderBox.Text = dialog.SelectedPath;
    }

    private void InstallButton_Click(object? sender, EventArgs e)
    {
        Install();
    }

    private void Install()
    {
        try
        {
            _installButton.Enabled = false;
            _progress.Style = ProgressBarStyle.Marquee;
            _statusLabel.Text = "Копирование файлов...";

            var installPath = _folderBox.Text.Trim();
            if (string.IsNullOrWhiteSpace(installPath))
                throw new InvalidOperationException("Выберите папку установки.");

            Directory.CreateDirectory(installPath);
            ExtractApplication(installPath);
            EnsureSettingsFile(installPath);

            if (_desktopShortcutBox.Checked)
                CreateShortcut("Автопрокат", Path.Combine(installPath, "AutorentWinForms.exe"));

            _progress.Style = ProgressBarStyle.Blocks;
            _progress.Value = 100;
            _statusLabel.Text = "Установка завершена.";

            MessageBox.Show(
                "Установка завершена. Приложение можно запустить через ярлык или файл AutorentWinForms.exe.",
                "Автопрокат",
                MessageBoxButtons.OK,
                MessageBoxIcon.Information);

            _installButton.Text = "Готово";
            _installButton.Enabled = true;
            _installButton.Click -= InstallButton_Click;
            _installButton.Click += (_, _) => Close();
        }
        catch (Exception ex)
        {
            _progress.Style = ProgressBarStyle.Blocks;
            _progress.Value = 0;
            _statusLabel.Text = "Ошибка установки.";
            _installButton.Enabled = true;
            MessageBox.Show(ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }

    private static void ExtractApplication(string installPath)
    {
        using var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("AppPackage.zip")
            ?? throw new InvalidOperationException("В установщик не встроен пакет приложения app.zip. Запустите scripts\\build_installer.ps1.");

        using var archive = new ZipArchive(stream, ZipArchiveMode.Read);
        foreach (var entry in archive.Entries)
        {
            var destinationPath = Path.GetFullPath(Path.Combine(installPath, entry.FullName));
            if (!destinationPath.StartsWith(Path.GetFullPath(installPath), StringComparison.OrdinalIgnoreCase))
                throw new InvalidOperationException("Некорректный путь внутри установочного пакета.");

            if (string.IsNullOrEmpty(entry.Name))
            {
                Directory.CreateDirectory(destinationPath);
                continue;
            }

            Directory.CreateDirectory(Path.GetDirectoryName(destinationPath)!);
            entry.ExtractToFile(destinationPath, overwrite: true);
        }
    }

    private static void EnsureSettingsFile(string installPath)
    {
        var settings = Path.Combine(installPath, "dbsettings.json");
        if (!File.Exists(settings))
        {
            File.WriteAllText(settings,
                """
                {
                  "ConnectionString": "Host=edu-pg.itiscaf.ru;Port=5432;Database=dbwork;Username=YOUR_LOGIN;Password=YOUR_PASSWORD;Search Path=YOUR_SCHEMA,public;Pooling=true;Timeout=15;Command Timeout=30"
                }
                """);
        }
    }

    private static void CreateShortcut(string name, string targetPath)
    {
        var desktop = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
        var shortcutPath = Path.Combine(desktop, name + ".lnk");

        var shellType = Type.GetTypeFromProgID("WScript.Shell");
        if (shellType is null)
            return;

        dynamic shell = Activator.CreateInstance(shellType)!;
        dynamic shortcut = shell.CreateShortcut(shortcutPath);
        shortcut.TargetPath = targetPath;
        shortcut.WorkingDirectory = Path.GetDirectoryName(targetPath);
        shortcut.Description = "Информационная система Автопрокат";
        shortcut.Save();

        Marshal.FinalReleaseComObject(shortcut);
        Marshal.FinalReleaseComObject(shell);
    }
}
