using System.Collections.Immutable;
using HermesSocketLibrary.db;
using HermesSocketServer.Models;

namespace HermesSocketServer.Store
{
    public class UserStore : GroupSaveStore<string, User>
    {
        private readonly Database _database;
        private readonly Serilog.ILogger _logger;
        private readonly GroupSaveSqlGenerator<User> _generator;


        public UserStore(Database database, Serilog.ILogger logger) : base(logger)
        {
            _database = database;
            _logger = logger;

            var ctp = new Dictionary<string, string>
            {
                { "id", "Id" },
                { "name", "Name" },
                { "email", "Email" },
                { "role", "Role" },
                { "ttsDefaultVoice", "DefaultVoice" }
            };
            _generator = new GroupSaveSqlGenerator<User>(ctp, _logger);
        }

        public override async Task Load()
        {
            string sql = "SELECT id, name, email, role, \"ttsDefaultVoice\" FROM \"User\";";
            await _database.Execute(sql, new Dictionary<string, object>(), (reader) =>
            {
                string id = reader.GetString(0);
                _store.Add(id, new User()
                {
                    Id = id,
                    Name = reader.GetString(1),
                    Email = reader.GetString(2),
                    Role = reader.GetString(3),
                    DefaultVoice = reader.GetString(4),
                });
            });
            _logger.Information($"Loaded {_store.Count} users from database.");
        }

        protected override void OnInitialAdd(string key, User value)
        {
        }

        protected override void OnInitialModify(string key, User value)
        {
        }

        protected override void OnInitialRemove(string key)
        {
        }

        public override async Task Save()
        {
            int count = 0;
            string sql = string.Empty;
            ImmutableList<string>? list = null;

            if (_added.Any())
            {
                lock (_lock)
                {
                    list = _added.ToImmutableList();
                    _added.Clear();
                }
                count = list.Count;
                sql = _generator.GeneratePreparedInsertSql("User", count, ["id", "name", "email", "role", "ttsDefaultVoice"]);

                _logger.Debug($"User - Adding {count} rows to database: {sql}");
                var values = list.Select(id => _store[id]).Where(v => v != null);
                await _generator.DoPreparedStatement(_database, sql, values, ["id", "name", "email", "role", "ttsDefaultVoice"]);
            }
            if (_modified.Any())
            {
                lock (_lock)
                {
                    list = _modified.ToImmutableList();
                    _modified.Clear();
                }
                count = list.Count;
                sql = _generator.GeneratePreparedUpdateSql("User", count, ["id"], ["name", "email", "role", "ttsDefaultVoice"]);

                _logger.Debug($"User - Modifying {count} rows in database: {sql}");
                var values = list.Select(id => _store[id]).Where(v => v != null);
                await _generator.DoPreparedStatement(_database, sql, values, ["id", "name", "email", "role", "ttsDefaultVoice"]);
            }
            if (_deleted.Any())
            {
                lock (_lock)
                {
                    list = _deleted.ToImmutableList();
                    _deleted.Clear();
                }
                count = list.Count;
                sql = _generator.GeneratePreparedDeleteSql("User", count, ["id"]);

                _logger.Debug($"User - Deleting {count} rows from database: {sql}");
                await _generator.DoPreparedStatementRaw(_database, sql, list, ["id"]);
            }
        }
    }
}