using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using ClaudeDo.Data.Environment;
using ClaudeDo.Installer.Checks;
using ClaudeDo.Installer.Core.Interfaces;

namespace ClaudeDo.Installer.Core;

public sealed record ClaudeHelpLaunchResult(bool Success, string? ErrorMessage);

/// <summary>
/// Builds a setup-diagnose report from the environment check results and opens an interactive
/// claude session pointed at it, so a stuck user can get live help finishing setup.
/// </summary>
public sealed class ClaudeHelpLauncher
{
    public const string ReportFileName = "claudedo-setup-diagnose.md";

    private readonly IProcessRunner _processRunner;
    private readonly IProcessLauncher _processLauncher;
    private readonly string? _pathOverride;
    private readonly string? _pathExtOverride;

    public ClaudeHelpLauncher(
        IProcessRunner processRunner,
        IProcessLauncher processLauncher,
        string? pathOverride = null,
        string? pathExtOverride = null)
    {
        _processRunner = processRunner;
        _processLauncher = processLauncher;
        _pathOverride = pathOverride;
        _pathExtOverride = pathExtOverride;
    }

    public async Task<string> BuildReportAsync(EnvironmentCheckReport report, InstallContext ctx, CancellationToken ct)
    {
        var sb = new StringBuilder();

        sb.AppendLine("# ClaudeDo setup diagnose");
        sb.AppendLine();
        sb.AppendLine(
            "ClaudeDo is a desktop task manager that runs the `claude` CLI autonomously in git " +
            "worktrees. The person you're talking to is stuck partway through the setup wizard. " +
            "Explain the likely cause of each failing check below in plain language, propose the " +
            "concrete command(s) to fix it, and ask a clarifying question if something here is " +
            "ambiguous.");
        sb.AppendLine();

        sb.AppendLine("## Check results");
        sb.AppendLine();
        sb.AppendLine("| Id | Severity | Status | Message |");
        sb.AppendLine("|---|---|---|---|");
        foreach (var result in report.Results)
        {
            sb.AppendLine($"| {result.Id} | {result.Severity} | {result.Status} | {EscapeCell(result.Message)} |");
        }
        sb.AppendLine();

        var failedDetails = report.Results
            .Where(r => r.Status == CheckStatus.Failed && !string.IsNullOrWhiteSpace(r.Detail))
            .ToList();
        if (failedDetails.Count > 0)
        {
            sb.AppendLine("## Details of failed checks");
            foreach (var result in failedDetails)
            {
                sb.AppendLine();
                sb.AppendLine($"### {result.Id}");
                sb.AppendLine("```");
                sb.AppendLine(result.Detail);
                sb.AppendLine("```");
            }
            sb.AppendLine();
        }

        sb.AppendLine("## System info");
        sb.AppendLine();
        sb.AppendLine($"- Windows: {RuntimeInformation.OSDescription}");
        sb.AppendLine($"- Architecture: {RuntimeInformation.OSArchitecture}");
        sb.AppendLine($"- git: {FindMessage(report, GitCheck.CheckId)}");
        sb.AppendLine($"- claude: {FindMessage(report, ClaudeCliCheck.CheckId)}");
        sb.AppendLine($"- Planned install directory: {ctx.InstallDirectory}");
        sb.AppendLine($"- Planned ports: SignalR {ctx.SignalRPort}, MCP {ctx.ExternalMcpPort}");
        sb.AppendLine("- `dotnet --list-runtimes`:");
        sb.AppendLine("```");
        sb.AppendLine(await RunDotnetListRuntimesAsync(ct));
        sb.AppendLine("```");
        sb.AppendLine();

        // No secrets: never include credentials.json content, tokens, or a raw environment
        // variable dump here — only resolved paths and version numbers, since this file exists
        // to be read by an AI assistant, not to stay private.
        sb.AppendLine(
            "No secrets are included above (no credentials file contents, no tokens, no raw " +
            "environment variable dump) — only resolved paths and version numbers.");
        sb.AppendLine();
        sb.AppendLine("Please start with the blocking errors above.");

        return sb.ToString();
    }

    public async Task<ClaudeHelpLaunchResult> LaunchAsync(EnvironmentCheckReport report, InstallContext ctx, CancellationToken ct)
    {
        string reportPath;
        try
        {
            var content = await BuildReportAsync(report, ctx, ct);
            reportPath = Path.Combine(Path.GetTempPath(), ReportFileName);
            await File.WriteAllTextAsync(reportPath, content, ct);
        }
        catch (Exception ex)
        {
            return new ClaudeHelpLaunchResult(false, ex.Message);
        }

        return LaunchTerminal(reportPath, ctx);
    }

    public ClaudeHelpLaunchResult LaunchTerminal(string reportPath, InstallContext ctx)
    {
        var claude = ExecutableResolver.Resolve(ctx.ClaudeBin, _pathOverride, _pathExtOverride);
        if (claude is null)
        {
            return new ClaudeHelpLaunchResult(false, $"'{ctx.ClaudeBin}' was not found on PATH.");
        }

        var tempDir = Path.GetTempPath();
        var promptText = $"Lies {reportPath} und hilf mir, mein ClaudeDo-Setup zum Laufen zu bringen.";
        var claudeCommand = $"{Quote(claude.Path)} {Quote(promptText)}";

        var wt = ExecutableResolver.Resolve("wt", _pathOverride, _pathExtOverride);
        var startInfo = wt is not null
            ? new ProcessStartInfo(wt.Path)
            {
                Arguments = $"-d {QuoteDirectory(tempDir)} cmd.exe /k {claudeCommand}",
                WorkingDirectory = tempDir,
                UseShellExecute = false,
            }
            : new ProcessStartInfo("cmd.exe")
            {
                Arguments = $"/k {claudeCommand}",
                WorkingDirectory = tempDir,
                UseShellExecute = false,
            };

        try
        {
            _processLauncher.Start(startInfo);
            return new ClaudeHelpLaunchResult(true, null);
        }
        catch (Exception ex)
        {
            return new ClaudeHelpLaunchResult(false, ex.Message);
        }
    }

    private async Task<string> RunDotnetListRuntimesAsync(CancellationToken ct)
    {
        try
        {
            var (_, output) = await _processRunner.RunAsync("dotnet", "--list-runtimes", null, ct);
            return output.Trim();
        }
        catch (Exception ex)
        {
            return $"(could not run 'dotnet --list-runtimes': {ex.Message})";
        }
    }

    private static string? FindMessage(EnvironmentCheckReport report, string checkId) =>
        report.Results.FirstOrDefault(r => r.Id == checkId)?.Message;

    private static string EscapeCell(string value) =>
        value.Replace("|", "\\|").Replace("\r", " ").Replace("\n", " ");

    private static string Quote(string value) => value.Contains(' ') ? $"\"{value}\"" : value;

    private static string QuoteDirectory(string directory) =>
        Quote(ClaudeDo.Data.Paths.TrimTrailingSeparator(directory)!);
}
