using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using beacon.service.Persisters;
using Mapster;

namespace beacon.service.Registries
{
    /// <summary>
    /// <see cref="IServiceRegistry"/>  implementation as ConcurrentDictionary.
    /// </summary>
    public class ServiceRegistry : IServiceRegistry
    {
        private readonly IServicePersister _persister;

        private readonly ConcurrentDictionary<Guid, ServiceInstance> _services =
            new ConcurrentDictionary<Guid, ServiceInstance>();

        /// <summary>
        /// Nuff said.
        /// </summary>
        /// <param name="persister"></param>
        public ServiceRegistry(IServicePersister persister) {
            _persister = persister;
            _persister.ReadAll(x => {
                var instance = new ServiceInstance(x.Uid, x.Key);
                x.Adapt(instance);
                _services[x.Uid] = instance;
            });
        }

        /// <inheritdoc />
        public void Dispose() {
            var records = _services.Values.Adapt<IEnumerable<ServiceInstance>, IEnumerable<ServiceRecord>>();
            _persister.RepersistAll(records);
        }

        /// <inheritdoc />
        public List<ServiceInstance> GetByKey(string key) {
            return _services.Values.Where(x => x.Key == key)
                .ToList();
        }

        /// <inheritdoc />
        public ServiceInstance GetByUid(Guid uid) {
            if (!_services.ContainsKey(uid))
                throw new KeyNotFoundException($"there is no service with uid={uid} in registry");
            return _services[uid];
        }

        /// <inheritdoc />
        public void Register(ServiceInstance serviceInstance) {
            if ((serviceInstance.Uid == null) || (serviceInstance.Uid == Guid.Empty))
                throw new ArgumentNullException("can't register service with empty uid");

            if (_services.ContainsKey(serviceInstance.Uid))
                throw new Exception($"service with uid={serviceInstance.Uid} alredy in registry");

            if (!_services.TryAdd(serviceInstance.Uid, serviceInstance))
                throw new Exception($"can't register  service with uid={serviceInstance.Uid}");
        }

        /// <inheritdoc />
        public void Unregister(Guid uid) {
            ServiceInstance service;
            _services.TryRemove(uid, out service);
        }

        /// <inheritdoc />
        public List<ServiceInstance> GetAll()
        {
            return _services.Values.ToList();
        }
    }
}