using System.Net;
using System.Net.Sockets;
using ClaudeDo.Installer.Checks.Interfaces;
using ClaudeDo.Installer.Core;

namespace ClaudeDo.Installer.Checks;

/// <summary>Both ports are configurable in the installer settings, so a conflict is only a warning.</summary>
public sealed class PortCheck : IEnvironmentCheck
{
    public const string CheckId = "ports";

    private readonly IPortOwnerResolver _portOwnerResolver;

    public PortCheck(IPortOwnerResolver portOwnerResolver) => _portOwnerResolver = portOwnerResolver;

    public string Id => CheckId;
    public CheckSeverity Severity => CheckSeverity.Warning;

    public async Task<CheckResult> RunAsync(InstallContext ctx, CancellationToken ct)
    {
        var ports = new (string Label, int Port)[]
        {
            ("SignalR", ctx.SignalRPort),
            ("External MCP", ctx.ExternalMcpPort),
        };

        var blocked = new List<string>();
        var ownWorker = new List<string>();

        foreach (var (label, port) in ports)
        {
            if (IsFree(port)) continue;

            var owner = await _portOwnerResolver.FindOwningProcessNameAsync(port, ct);
            if (owner is not null && owner.Contains("ClaudeDo.Worker", StringComparison.OrdinalIgnoreCase))
            {
                ownWorker.Add($"{label} ({port})");
                continue;
            }

            blocked.Add(owner is null ? $"{label} port {port} is in use." : $"{label} port {port} is in use by '{owner}'.");
        }

        if (blocked.Count > 0)
        {
            return CheckResult.Fail(Id, Severity, "checks.ports.title", string.Join(" ", blocked), "checks.ports.hint");
        }

        if (ownWorker.Count > 0)
        {
            return CheckResult.Ok(Id, Severity, "checks.ports.title", $"In use by the running ClaudeDo Worker: {string.Join(", ", ownWorker)}.");
        }

        return CheckResult.Ok(Id, Severity, "checks.ports.title", "Ports are available.");
    }

    private static bool IsFree(int port)
    {
        try
        {
            using var listener = new TcpListener(IPAddress.Loopback, port);
            listener.Start();
            return true;
        }
        catch (SocketException)
        {
            return false;
        }
    }
}
