using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using Sdt.Config;

namespace Sdt.Core.Debug;

public sealed class DiagnosticsBundleService : IDiagnosticsBundleService
{
    private static readonly JsonSerializerOptions JsonOptions = new()
    {
        WriteIndented = true
    };

    public async Task<DiagnosticsBundleResult> WriteBundleAsync(
        DiagnosticsBundleRequest request,
        CancellationToken cancellationToken = default)
    {
        try
        {
            var timestamp = DateTimeOffset.Now.ToString("yyyyMMdd-HHmmss");
            var root = Path.GetFullPath(Path.Combine(request.ProjectRoot, request.DiagnosticsOptions.OutputDir));
            var bundleDir = Path.Combine(root, $"{request.Category}-{timestamp}");
            Directory.CreateDirectory(bundleDir);

            var stepsPath = Path.Combine(bundleDir, "steps.json");
            var toolsPath = Path.Combine(bundleDir, "tools.json");
            var envPath = Path.Combine(bundleDir, "env.json");
            var outputPath = Path.Combine(bundleDir, "output.log");
            var summaryPath = Path.Combine(bundleDir, "summary.json");

            await File.WriteAllTextAsync(stepsPath, JsonSerializer.Serialize(request.WorkflowSteps, JsonOptions), cancellationToken);
            await File.WriteAllTextAsync(toolsPath, JsonSerializer.Serialize(request.Probes, JsonOptions), cancellationToken);
            await File.WriteAllTextAsync(envPath, JsonSerializer.Serialize(CaptureEnvironment(request.DiagnosticsOptions), JsonOptions), cancellationToken);
            await File.WriteAllLinesAsync(outputPath, request.OutputLines, cancellationToken);

            var summary = new
            {
                category = request.Category,
                stopReason = request.StopReason?.ToString(),
                message = request.SummaryMessage,
                createdAt = DateTimeOffset.Now,
                configHash = HashConfig(request.Config),
                debugProfile = request.DebugProfile?.Id,
                debugExitCode = request.DebugRun?.ExitCode,
                envCapture = BuildEnvCaptureSummary(request.DiagnosticsOptions),
            };
            await File.WriteAllTextAsync(summaryPath, JsonSerializer.Serialize(summary, JsonOptions), cancellationToken);

            return new DiagnosticsBundleResult(
                Success: true,
                BundleDirectory: bundleDir,
                ZipPath: null,
                Message: "Diagnostics bundle generated.");
        }
        catch (Exception ex)
        {
            return new DiagnosticsBundleResult(
                Success: false,
                BundleDirectory: string.Empty,
                ZipPath: null,
                Message: ex.Message);
        }
    }

    private static Dictionary<string, string> CaptureEnvironment(DebugDiagnosticsOptions options)
    {
        if (options.IncludeAllEnv)
        {
            return Environment.GetEnvironmentVariables()
                .Cast<System.Collections.DictionaryEntry>()
                .ToDictionary(e => e.Key?.ToString() ?? "", e => e.Value?.ToString() ?? "", StringComparer.OrdinalIgnoreCase);
        }

        if (options.CaptureEnvKeys.Count == 0)
            return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

        var captured = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
        foreach (var key in options.CaptureEnvKeys)
            captured[key] = Environment.GetEnvironmentVariable(key) ?? "";
        return captured;
    }

    private static string BuildEnvCaptureSummary(DebugDiagnosticsOptions options)
    {
        if (options.IncludeAllEnv)
            return "full";
        if (options.CaptureEnvKeys.Count == 0)
            return "allowlist-empty (intentional minimal capture)";
        return $"allowlist ({options.CaptureEnvKeys.Count} keys)";
    }

    private static string HashConfig(object config)
    {
        var json = JsonSerializer.Serialize(config);
        var bytes = SHA256.HashData(Encoding.UTF8.GetBytes(json));
        return Convert.ToHexString(bytes);
    }
}
