using System.Runtime.InteropServices;

namespace YMhut.Box.WinUI.Services;

internal static class LanguagePackService
{
    private const string ProjectionMarkerFileName = ".ymhut-transient-lang";
    private static readonly (string SourceCulture, string RootCulture)[] WinUiMuiProjectionCultures =
    [
        ("zh-CN", "zh-CN"),
        ("en-US", "en-us")
    ];

    public static bool EnsureLanguagePacksExtracted()
    {
        try
        {
            if (HasLegacyCompressedLayout() && !HasRootMuiProjection())
            {
                ShowStartupError(
                    "YMhut Box 启动失败",
                    "检测到旧版压缩语言资源布局，且当前安装目录没有可用的 WinUI 语言资源。\n\n" +
                    "请使用新版安装包覆盖安装，安装引导程序会清理旧布局并重新铺设程序文件。\n\n" +
                    "Legacy compressed language resources were detected. Please reinstall or repair YMhut Box with the latest setup package.");
                return false;
            }

            return EnsureTransientWinUiMuiProjection();
        }
        catch (Exception exception)
        {
            CrashLog.Write(exception);
            ShowStartupError(
                "YMhut Box 启动失败",
                "YMhut Box 无法准备 WinUI 语言资源。\n\n" +
                "安装目录可能不可写，或 lang\\zh-CN / lang\\en-US 语言文件缺失。请使用安装器修复，或安装到当前用户可写的目录。\n\n" +
                "YMhut Box could not prepare WinUI language resources. Repair the installation or install to a writable directory.");
            return false;
        }
    }

    public static void ScheduleTransientProjectionCleanup()
    {
        _ = Task.Run(async () =>
        {
            try
            {
                await Task.Delay(TimeSpan.FromSeconds(10)).ConfigureAwait(false);
                CleanupTransientProjection();
            }
            catch (Exception exception)
            {
                CrashLog.Write($"清理临时语言资源映射失败: {exception.Message}");
            }
        });
    }

    private static bool HasLegacyCompressedLayout()
    {
        var legacyLanguageRoot = Path.Combine(AppContext.BaseDirectory, "resources", "lang");
        return Directory.Exists(legacyLanguageRoot) &&
            Directory.EnumerateFiles(legacyLanguageRoot, "*.bin", SearchOption.TopDirectoryOnly).Any();
    }

    private static bool EnsureTransientWinUiMuiProjection()
    {
        var baseDirectory = Path.GetFullPath(AppContext.BaseDirectory);
        var langRoot = Path.Combine(baseDirectory, "lang");
        if (!Directory.Exists(langRoot))
        {
            return true;
        }

        if (HasRootMuiProjection())
        {
            return true;
        }

        if (!CanWriteDirectory(baseDirectory))
        {
            ShowStartupError(
                "YMhut Box 启动失败",
                "YMhut Box 无法准备 WinUI 语言资源。\n\n" +
                $"安装目录不可写：{baseDirectory}\n\n" +
                "请使用安装器修复，或安装到当前用户可写的目录。");
            return false;
        }

        foreach (var (sourceCulture, rootCulture) in WinUiMuiProjectionCultures)
        {
            var source = Path.Combine(langRoot, sourceCulture);
            if (!Directory.Exists(source))
            {
                continue;
            }

            var target = Path.Combine(baseDirectory, rootCulture);
            if (!IsPathInsideBase(baseDirectory, target) || HasMuiFiles(target))
            {
                continue;
            }

            Directory.CreateDirectory(target);
            CopyDirectory(source, target);
            File.WriteAllText(Path.Combine(target, ProjectionMarkerFileName), DateTimeOffset.Now.ToString("O"));
            TrySetHidden(target);
        }

        return HasRootMuiProjection();
    }

    private static bool CanWriteDirectory(string directory)
    {
        try
        {
            Directory.CreateDirectory(directory);
            var probe = Path.Combine(directory, $".ymhut-write-test-{Guid.NewGuid():N}.tmp");
            File.WriteAllText(probe, string.Empty);
            File.Delete(probe);
            return true;
        }
        catch (Exception exception)
        {
            CrashLog.Write($"Install directory is not writable: {directory}. {exception.Message}");
            return false;
        }
    }

    private static void CleanupTransientProjection()
    {
        var baseDirectory = Path.GetFullPath(AppContext.BaseDirectory);
        foreach (var (_, rootCulture) in WinUiMuiProjectionCultures)
        {
            var target = Path.Combine(baseDirectory, rootCulture);
            if (!IsPathInsideBase(baseDirectory, target))
            {
                continue;
            }

            var marker = Path.Combine(target, ProjectionMarkerFileName);
            if (!File.Exists(marker))
            {
                continue;
            }

            TryNormalizeAttributes(target);
            Directory.Delete(target, recursive: true);
        }
    }

    private static void CopyDirectory(string source, string target)
    {
        foreach (var directory in Directory.EnumerateDirectories(source, "*", SearchOption.AllDirectories))
        {
            var relative = Path.GetRelativePath(source, directory);
            Directory.CreateDirectory(Path.Combine(target, relative));
        }

        foreach (var file in Directory.EnumerateFiles(source, "*", SearchOption.AllDirectories))
        {
            var relative = Path.GetRelativePath(source, file);
            var destination = Path.Combine(target, relative);
            Directory.CreateDirectory(Path.GetDirectoryName(destination)!);
            File.Copy(file, destination, overwrite: true);
        }
    }

    private static bool HasRootMuiProjection()
    {
        var baseDirectory = Path.GetFullPath(AppContext.BaseDirectory);
        return WinUiMuiProjectionCultures.Any(culture => HasMuiFiles(Path.Combine(baseDirectory, culture.RootCulture)));
    }

    private static bool HasMuiFiles(string directory)
    {
        return Directory.Exists(directory) &&
            Directory.EnumerateFiles(directory, "*.mui", SearchOption.TopDirectoryOnly).Any();
    }

    private static bool IsPathInsideBase(string baseDirectory, string candidate)
    {
        var full = Path.GetFullPath(candidate);
        return full.StartsWith(baseDirectory, StringComparison.OrdinalIgnoreCase) &&
            !string.Equals(full, baseDirectory, StringComparison.OrdinalIgnoreCase);
    }

    private static void TrySetHidden(string directory)
    {
        try
        {
            File.SetAttributes(directory, File.GetAttributes(directory) | FileAttributes.Hidden);
        }
        catch
        {
        }
    }

    private static void TryNormalizeAttributes(string directory)
    {
        try
        {
            foreach (var path in Directory.EnumerateFileSystemEntries(directory, "*", SearchOption.AllDirectories))
            {
                File.SetAttributes(path, FileAttributes.Normal);
            }

            File.SetAttributes(directory, FileAttributes.Normal);
        }
        catch
        {
        }
    }

    private static void ShowStartupError(string title, string message)
    {
        CrashLog.Write(message);
        try
        {
            _ = MessageBoxW(IntPtr.Zero, message, title, 0x00000010);
        }
        catch
        {
        }
    }

    [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = false)]
    private static extern int MessageBoxW(IntPtr hWnd, string text, string caption, uint type);
}
