using Microsoft.Data.Sqlite;
using OF_DL.Models.Entities.Users;

namespace OF_DL.Services;

public class CajetanDbService(IConfigService configService)
    : DbService(configService), ICajetanDbService
{
    public async Task InitializeUserInfoTablesAsync()
    {
        await using SqliteConnection connection = new($"Data Source={Directory.GetCurrentDirectory()}/users.db");


        using (SqliteCommand cmdInfo = new("CREATE TABLE IF NOT EXISTS user_info (user_id INTEGER NOT NULL, name VARCHAR NOT NULL, about VARCHAR NULL, expires_on TIMESTAMP NULL, photo_count INT NOT NULL, video_count INT NOT NULL, PRIMARY KEY(user_id));", connection))
        {
            await cmdInfo.ExecuteNonQueryAsync();
        }

        using (SqliteCommand cmdInfo = new("CREATE TABLE IF NOT EXISTS user_info_blob (user_id INTEGER NOT NULL, name VARCHAR NOT NULL, blob TEXT NULL, PRIMARY KEY(user_id));", connection))
        {
            await cmdInfo.ExecuteNonQueryAsync();
        }

    }

    public async Task<Dictionary<string, long>> GetUsersAsync()
    {
        await using SqliteConnection connection = new($"Data Source={Directory.GetCurrentDirectory()}/users.db");

        using SqliteCommand cmd = new("SELECT user_id, username FROM users", connection);
        using SqliteDataReader reader = cmd.ExecuteReader();

        Dictionary<string, long> result = new(StringComparer.OrdinalIgnoreCase);

        while (reader.Read())
        {
            long userId = reader.GetInt64(0);
            string username = reader.GetString(1);

            result[username] = userId;
        }

        return result;
    }

    public async Task UpdateUserInfoAsync(UserInfo? userInfo)
    {
        if (userInfo?.Id is null || userInfo?.Username is null)
            return;

        await using SqliteConnection connection = new($"Data Source={Directory.GetCurrentDirectory()}/users.db");

        Log.Debug("Database data source: " + connection.DataSource);

        await UpdateAsync();
        await UpdateBlobAsync();

        async Task UpdateAsync()
        {
            using SqliteCommand cmdInfo = new(
                "INSERT OR REPLACE INTO user_info (user_id, name, about, expires_on, photo_count, video_count) " +
                "VALUES (@userId, @name, @about, @expiresOn, @photoCount, @videoCount);",
                connection
            );

            cmdInfo.Parameters.AddWithValue("@userId", userInfo.Id);
            cmdInfo.Parameters.AddWithValue("@name", userInfo.Name ?? userInfo.Username);
            cmdInfo.Parameters.AddWithValue("@about", userInfo.About);
            cmdInfo.Parameters.AddWithValue("@expiresOn", userInfo.SubscribedByExpireDate);
            cmdInfo.Parameters.AddWithValue("@photoCount", userInfo.PhotosCount ?? 0);
            cmdInfo.Parameters.AddWithValue("@videoCount", userInfo.VideosCount ?? 0);

            try
            {
                await cmdInfo.ExecuteNonQueryAsync();
                Log.Debug("Inserted or updated creator info: {Username:l}", userInfo.Username);
            }
            catch (Exception ex)
            {
                Log.Warning(ex, "Failed to update User Info for: {Username:l}", userInfo.Username);
            }
        }

        async Task UpdateBlobAsync()
        {
            using SqliteCommand cmdInfo = new(
                "INSERT OR REPLACE INTO user_info_blob (user_id, name, blob) " +
                "VALUES (@userId, @name, @blob);",
                connection
            );

            cmdInfo.Parameters.AddWithValue("@userId", userInfo.Id);
            cmdInfo.Parameters.AddWithValue("@name", userInfo.Name ?? userInfo.Username);
            cmdInfo.Parameters.AddWithValue("@blob", Newtonsoft.Json.JsonConvert.SerializeObject(userInfo));

            try
            {
                await cmdInfo.ExecuteNonQueryAsync();
                Log.Debug("Inserted or updated creator blob: {Username:l}", userInfo.Username);
            }
            catch (Exception ex)
            {
                Log.Warning(ex, "Failed to update User Info Blob for: {Username:l}", userInfo.Username);
            }
        }
    }
}
