using RackPeek.Domain.Resources; using RackPeek.Domain.Resources.AccessPoints; using RackPeek.Domain.Resources.Desktops; using RackPeek.Domain.Resources.Firewalls; using RackPeek.Domain.Resources.Hardware; using RackPeek.Domain.Resources.Laptops; using RackPeek.Domain.Resources.Routers; using RackPeek.Domain.Resources.Servers; using RackPeek.Domain.Resources.Services; using RackPeek.Domain.Resources.Switches; using RackPeek.Domain.Resources.SystemResources; using RackPeek.Domain.Resources.UpsUnits; namespace RackPeek.Domain.Persistence; public sealed class InMemoryResourceCollection(IEnumerable? seed = null) : IResourceCollection { private readonly object _lock = new(); private readonly List _resources = seed?.ToList() ?? []; public IReadOnlyList HardwareResources { get { lock (_lock) { return _resources.OfType().ToList(); } } } public IReadOnlyList SystemResources { get { lock (_lock) { return _resources.OfType().ToList(); } } } public IReadOnlyList ServiceResources { get { lock (_lock) { return _resources.OfType().ToList(); } } } public Task Exists(string name) { lock (_lock) { return Task.FromResult(_resources.Exists(r => r.Name.Equals(name, StringComparison.OrdinalIgnoreCase))); } } public Task GetKind(string name) { lock (_lock) { return Task.FromResult(_resources.FirstOrDefault(r => r.Name.Equals(name, StringComparison.OrdinalIgnoreCase))?.Kind); } } public Task LoadAsync() { return Task.CompletedTask; } public Task> GetByTagAsync(string name) { lock (_lock) { return Task.FromResult>(_resources.Where(r => r.Tags.Contains(name)).ToList()); } } public Task> GetTagsAsync() { lock (_lock) { var result = _resources .SelectMany(r => r.Tags!) // flatten all tag arrays .Where(t => !string.IsNullOrWhiteSpace(t)) .GroupBy(t => t) .ToDictionary(g => g.Key, g => g.Count()); return Task.FromResult(result); } } public Task> GetByLabelAsync(string name) { lock (_lock) { var result = _resources .Select(r => { if (r.Labels != null && r.Labels.TryGetValue(name, out var value)) return (found: true, pair: (r, value)); return (found: false, pair: default); }) .Where(x => x.found) .Select(x => x.pair) .ToList() .AsReadOnly(); return Task.FromResult>(result); } } public Task> GetLabelsAsync() { lock (_lock) { var result = _resources .SelectMany(r => r.Labels ?? Enumerable.Empty>()) .Where(kvp => !string.IsNullOrWhiteSpace(kvp.Key)) .GroupBy(kvp => kvp.Key) .ToDictionary(g => g.Key, g => g.Count()); return Task.FromResult(result); } } public Task> GetResourceIpsAsync() { lock (_lock) { var result = new List<(Resource, string)>(); var allResources = _resources; var systemsByName = allResources .OfType() .ToDictionary(s => s.Name, StringComparer.OrdinalIgnoreCase); // Cache resolved system IPs (prevents repeated recursion) var resolvedSystemIps = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (var resource in allResources) { switch (resource) { case SystemResource system: { var ip = ResolveSystemIp(system, systemsByName, resolvedSystemIps); if (!string.IsNullOrWhiteSpace(ip)) result.Add((system, ip)); break; } case Service service: { var ip = ResolveServiceIp(service, systemsByName, resolvedSystemIps); if (!string.IsNullOrWhiteSpace(ip)) result.Add((service, ip)); break; } } } return Task.FromResult((IReadOnlyList<(Resource, string)>)result); } } private string? ResolveSystemIp( SystemResource system, Dictionary systemsByName, Dictionary cache) { // Return cached result if already resolved if (cache.TryGetValue(system.Name, out var cached)) return cached; // Direct IP wins if (!string.IsNullOrWhiteSpace(system.Ip)) { cache[system.Name] = system.Ip; return system.Ip; } // Must have exactly one parent if (system.RunsOn?.Count != 1) { cache[system.Name] = null; return null; } var parentName = system.RunsOn.First(); if (!systemsByName.TryGetValue(parentName, out var parent)) { cache[system.Name] = null; return null; } var resolved = ResolveSystemIp(parent, systemsByName, cache); cache[system.Name] = resolved; return resolved; } private string? ResolveServiceIp( Service service, Dictionary systemsByName, Dictionary cache) { // Direct IP wins if (!string.IsNullOrWhiteSpace(service.Network?.Ip)) return service.Network!.Ip; // Must have exactly one parent if (service.RunsOn?.Count != 1) return null; var parentName = service.RunsOn.First(); if (!systemsByName.TryGetValue(parentName, out var parent)) return null; return ResolveSystemIp(parent, systemsByName, cache); } public Task> GetAllOfTypeAsync() { lock (_lock) { return Task.FromResult>(_resources.OfType().ToList()); } } public Task> GetDependantsAsync(string name) { lock (_lock) { return Task.FromResult>(_resources .Where(r => r.RunsOn.Select(p => p.Equals(name, StringComparison.OrdinalIgnoreCase)).ToList().Count != 0).ToList()); } } public Task AddAsync(Resource resource) { lock (_lock) { if (_resources.Any(r => r.Name.Equals(resource.Name, StringComparison.OrdinalIgnoreCase))) throw new InvalidOperationException($"'{resource.Name}' already exists."); resource.Kind = GetKind(resource); _resources.Add(resource); } return Task.CompletedTask; } public Task UpdateAsync(Resource resource) { lock (_lock) { var index = _resources.FindIndex(r => r.Name.Equals(resource.Name, StringComparison.OrdinalIgnoreCase)); if (index == -1) throw new InvalidOperationException("Not found."); resource.Kind = GetKind(resource); _resources[index] = resource; } return Task.CompletedTask; } public Task DeleteAsync(string name) { lock (_lock) { _resources.RemoveAll(r => r.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); } return Task.CompletedTask; } public Task GetByNameAsync(string name) { lock (_lock) { return Task.FromResult(_resources.FirstOrDefault(r => r.Name.Equals(name, StringComparison.OrdinalIgnoreCase))); } } public Task GetByNameAsync(string name) where T : Resource { lock (_lock) { var resource = _resources.FirstOrDefault(r => r.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); return Task.FromResult(resource as T); } } public Resource? GetByName(string name) { lock (_lock) { return _resources.FirstOrDefault(r => r.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); } } private static string GetKind(Resource resource) { return resource switch { Server => "Server", Switch => "Switch", Firewall => "Firewall", Router => "Router", Desktop => "Desktop", Laptop => "Laptop", AccessPoint => "AccessPoint", Ups => "Ups", SystemResource => "System", Service => "Service", _ => throw new InvalidOperationException( $"Unknown resource type: {resource.GetType().Name}") }; } }