using System.IO;
using System.IO.Compression;
using System.Reflection;
using System.Text;

namespace DragonISO.App.Services;

/// <summary>
/// Gathers logs + config + presets + version metadata into a single .zip the
/// operator can attach to a bug report. Surfaced via the "Export diagnostics"
/// button in About.
///
/// We deliberately do NOT include screenshots or any process/memory dumps â€”
/// that's outside the scope of a v1 support bundle and would raise privacy
/// flags. The bundle has only files the user already wrote with their Dragon-ISO
/// usage; nothing here is hidden state.
/// </summary>
public static class DiagnosticsBundle
{
    /// <summary>
    /// Build the bundle and return the path it was written to.
    /// Throws on disk failure â€” the caller toasts/dialogs.
    /// </summary>
    public static string Export()
    {
        var ts = DateTimeOffset.Now.ToString("yyyyMMdd-HHmmss");
        var fileName = $"Dragon-ISO-diagnostics-{ts}.zip";
        var outDir = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
        var downloads = Path.Combine(outDir, "Downloads");
        if (!Directory.Exists(downloads)) downloads = outDir; // fall back to ~/
        var outPath = Path.Combine(downloads, fileName);

        using var fs = new FileStream(outPath, FileMode.Create, FileAccess.Write, FileShare.None);
        using var zip = new ZipArchive(fs, ZipArchiveMode.Create, leaveOpen: false);

        WriteEnvironmentTxt(zip);
        TryCopyDirectory(zip, "logs", LogsDirectory);
        TryCopyFile(zip, "config.json", AppDataPath("config.json"));
        TryCopyFile(zip, "presets.json", LocalAppDataPath("presets.json"));
        TryCopyFile(zip, "window.json", LocalAppDataPath("window.json"));
        TryCopyFile(zip, "ndi-config.v1.json", NdiConfigPath());
        TryCopyFile(zip, "output-name-template.txt", LocalAppDataPath("output-name-template.txt"));

        return outPath;
    }

    private static void WriteEnvironmentTxt(ZipArchive zip)
    {
        var asm = typeof(DiagnosticsBundle).Assembly;
        var version = asm.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion
            ?? asm.GetName().Version?.ToString()
            ?? "unknown";
        var sb = new StringBuilder();
        sb.AppendLine("Dragon-ISO diagnostic bundle");
        sb.AppendLine($"Generated:        {DateTimeOffset.Now:o}");
        sb.AppendLine($"Dragon-ISO version: {version}");
        sb.AppendLine($".NET runtime:     {Environment.Version}");
        sb.AppendLine($"OS:               {Environment.OSVersion}");
        sb.AppendLine($"Machine:          {Environment.MachineName}");
        sb.AppendLine($"User:             {Environment.UserName}");
        sb.AppendLine($"Process bits:     {(Environment.Is64BitProcess ? "64" : "32")}");
        sb.AppendLine($"OS bits:          {(Environment.Is64BitOperatingSystem ? "64" : "32")}");
        sb.AppendLine($"Working set:      {Environment.WorkingSet / (1024 * 1024)} MB");
        sb.AppendLine();
        sb.AppendLine("Files included (when present):");
        sb.AppendLine("  logs/                Serilog rolling daily logs");
        sb.AppendLine("  config.json          Engine settings (framerate, NDI groups, etc.)");
        sb.AppendLine("  presets.json         Saved operator presets");
        sb.AppendLine("  window.json          Last main-window placement");
        sb.AppendLine("  ndi-config.v1.json   NDI Access Manager config (group routing)");
        sb.AppendLine("  output-name-template.txt   NDI source name template override");

        var entry = zip.CreateEntry("environment.txt");
        using var w = new StreamWriter(entry.Open(), Encoding.UTF8);
        w.Write(sb.ToString());
    }

    private static void TryCopyFile(ZipArchive zip, string entryName, string sourcePath)
    {
        try
        {
            if (!File.Exists(sourcePath)) return;
            zip.CreateEntryFromFile(sourcePath, entryName);
        }
        catch
        {
            // One missing or locked file shouldn't kill the rest of the bundle.
        }
    }

    private static void TryCopyDirectory(ZipArchive zip, string prefix, string sourceDir)
    {
        if (!Directory.Exists(sourceDir)) return;
        try
        {
            foreach (var file in Directory.EnumerateFiles(sourceDir, "*", SearchOption.AllDirectories))
            {
                try
                {
                    var rel = Path.GetRelativePath(sourceDir, file).Replace('\\', '/');
                    zip.CreateEntryFromFile(file, $"{prefix}/{rel}");
                }
                catch
                {
                    // Skip locked files (e.g., today's actively-written log).
                }
            }
        }
        catch
        {
            // Permission denied on the dir as a whole; nothing more to do.
        }
    }

    private static string LogsDirectory =>
        Path.Combine(
            Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
            "Dragon-ISO", "Logs");

    private static string LocalAppDataPath(string fileName) =>
        Path.Combine(
            Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
            "Dragon-ISO", fileName);

    private static string AppDataPath(string fileName) =>
        Path.Combine(
            Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
            "Dragon-ISO", fileName);

    private static string NdiConfigPath() =>
        Path.Combine(
            Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
            "NDI", "ndi-config.v1.json");
}
