using Microsoft.EntityFrameworkCore;

/// <summary>
/// One-shot / idempotent registration of a clinical site + ward gateway with
/// caller-supplied IDs. Production cannot rely on GatewayRegistrySeeder (demo
/// data is gated off); the gateway authenticates using Gateway:GatewayId /
/// Gateway:SiteId from its environment, so those GUIDs must exist in the API DB.
///
/// Usage:
///   dotnet VigilCareClinicalAPI.dll register-gateway \
///     --site-id &lt;guid&gt; --gateway-id &lt;guid&gt; \
///     --site-code SITE-01 --site-name "General Hospital" \
///     --gateway-code GW-ICU-1 --department ICU \
///     [--address "123 Main St"]
///
/// Re-running with the same IDs is a no-op success. Conflicting codes or IDs fail.
/// </summary>
public static class RegisterGatewayCommand
{
    public static async Task<int> RunAsync(IServiceProvider services, string[] args)
    {
        var siteId = RequireGuid(args, "--site-id");
        var gatewayId = RequireGuid(args, "--gateway-id");
        var siteCode = RequireArg(args, "--site-code").Trim();
        var siteName = RequireArg(args, "--site-name").Trim();
        var gatewayCode = RequireArg(args, "--gateway-code").Trim();
        var department = RequireArg(args, "--department").Trim();
        var address = GetArg(args, "--address")?.Trim();

        using var scope = services.CreateScope();
        var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();

        var existingGateway = await db.WardGateways
            .Include(g => g.Site)
            .FirstOrDefaultAsync(g => g.Id == gatewayId);

        if (existingGateway is not null)
        {
            if (existingGateway.SiteId != siteId
                || !string.Equals(existingGateway.GatewayCode, gatewayCode, StringComparison.Ordinal)
                || !string.Equals(existingGateway.Department, department, StringComparison.Ordinal))
            {
                Console.Error.WriteLine(
                    $"Gateway id {gatewayId} already exists with different site/code/department. Aborting.");
                return 1;
            }

            Console.WriteLine(
                $"Gateway '{existingGateway.GatewayCode}' (id={gatewayId}) already registered — nothing to do.");
            return 0;
        }

        var existingSite = await db.ClinicalSites.FirstOrDefaultAsync(s => s.Id == siteId);
        if (existingSite is null)
        {
            var codeTaken = await db.ClinicalSites.AnyAsync(s => s.SiteCode == siteCode);
            if (codeTaken)
            {
                Console.Error.WriteLine($"Site code '{siteCode}' is already registered under a different id.");
                return 1;
            }

            var site = new ClinicalSite(siteCode, siteName, address);
            db.Entry(site).Property(nameof(ClinicalSite.Id)).CurrentValue = siteId;
            db.ClinicalSites.Add(site);
            Console.WriteLine($"Created site '{siteCode}' (id={siteId}).");
        }
        else if (!string.Equals(existingSite.SiteCode, siteCode, StringComparison.Ordinal))
        {
            Console.Error.WriteLine(
                $"Site id {siteId} already exists as code '{existingSite.SiteCode}', " +
                $"not '{siteCode}'. Aborting.");
            return 1;
        }

        var duplicateCode = await db.WardGateways.AnyAsync(g =>
            g.SiteId == siteId && g.GatewayCode == gatewayCode);
        if (duplicateCode)
        {
            Console.Error.WriteLine(
                $"Gateway code '{gatewayCode}' already exists for site {siteId} under a different id.");
            return 1;
        }

        var gateway = new WardGateway(siteId, gatewayCode, department);
        db.Entry(gateway).Property(nameof(WardGateway.Id)).CurrentValue = gatewayId;
        db.WardGateways.Add(gateway);
        await db.SaveChangesAsync();

        Console.WriteLine($"Registered gateway '{gatewayCode}' (id={gatewayId}) on site {siteId}.");
        return 0;
    }

    private static Guid RequireGuid(string[] args, string name)
    {
        var raw = RequireArg(args, name);
        if (!Guid.TryParse(raw, out var id) || id == Guid.Empty)
            throw new ArgumentException($"{name} must be a non-empty GUID.");
        return id;
    }

    private static string RequireArg(string[] args, string name)
    {
        var value = GetArg(args, name);
        if (string.IsNullOrWhiteSpace(value))
            throw new ArgumentException($"Missing required argument: {name}");
        return value;
    }

    private static string? GetArg(string[] args, string name)
    {
        for (var i = 0; i < args.Length - 1; i++)
        {
            if (string.Equals(args[i], name, StringComparison.OrdinalIgnoreCase))
            {
                var value = args[i + 1];
                if (string.IsNullOrWhiteSpace(value) || value.StartsWith("--", StringComparison.Ordinal))
                    return null;
                return value;
            }
        }

        return null;
    }
}
