// Copyright (C) 2021-present sam (starshines.gay)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

using System.Text.Json;
using StackExchange.Redis;

namespace Catalogger.Backend.Database.Redis;

public class RedisService(Config config)
{
    private readonly ConnectionMultiplexer _multiplexer = ConnectionMultiplexer.Connect(
        config.Database.Redis!
    );

    private readonly JsonSerializerOptions _options =
        new() { PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower };

    public IDatabase GetDatabase(int db = -1) => _multiplexer.GetDatabase(db);

    public async Task SetStringAsync(string key, string value, TimeSpan? expiry = null) =>
        await GetDatabase().StringSetAsync(key, value, expiry);

    public async Task<string?> GetStringAsync(string key, bool delete = false)
    {
        var db = GetDatabase();
        return delete ? await db.StringGetDeleteAsync(key) : await db.StringGetAsync(key);
    }

    public async Task SetAsync<T>(string key, T value, TimeSpan? expiry = null)
    {
        var json = JsonSerializer.Serialize(value, _options);
        await GetDatabase().StringSetAsync(key, json, expiry);
    }

    public async Task DeleteAsync(string[] keys) =>
        await GetDatabase().KeyDeleteAsync(keys.Select(k => new RedisKey(k)).ToArray());

    public async Task<T?> GetAsync<T>(string key)
    {
        var value = await GetDatabase().StringGetAsync(key);
        return value.IsNull ? default : JsonSerializer.Deserialize<T>(value!, _options);
    }

    public async Task SetHashAsync<T>(string hashKey, string fieldKey, T value)
    {
        var json = JsonSerializer.Serialize(value, _options);
        await GetDatabase().HashSetAsync(hashKey, fieldKey, json);
    }

    public async Task SetHashAsync<T>(
        string hashKey,
        IEnumerable<T> values,
        Func<T, string> keySelector
    )
    {
        var hashEntries = values
            .Select(v => new
            {
                Key = keySelector(v),
                Value = JsonSerializer.Serialize(v, _options),
            })
            .Select(v => new HashEntry(v.Key, v.Value));
        await GetDatabase().HashSetAsync(hashKey, hashEntries.ToArray());
    }

    public async Task<T?> GetHashAsync<T>(string hashKey, string fieldKey)
    {
        var value = await GetDatabase().HashGetAsync(hashKey, fieldKey);
        return value.IsNull ? default : JsonSerializer.Deserialize<T>(value!, _options);
    }
}
