using System.Security.Cryptography;
using Microsoft.Data.Sqlite;
namespace ytLive.Services;
public partial class LayoutStore : IDisposable
{
public byte[]? GetAssetBytes(string assetId)
{
using var cmd = _connection.CreateCommand();
cmd.CommandText = "SELECT Data FROM Asset WHERE Id = $id";
cmd.Parameters.AddWithValue("$id", assetId);
return cmd.ExecuteScalar() as byte[];
}
public string UpsertAsset(byte[] data, int pixelWidth, int pixelHeight)
{
var hash = Convert.ToHexString(SHA256.HashData(data));
var id = Guid.NewGuid().ToString();
using var cmd = _connection.CreateCommand();
cmd.CommandText = """
INSERT INTO Asset (Id, Hash, Data, PixelWidth, PixelHeight)
VALUES ($id, $hash, $data, $w, $h)
ON CONFLICT(Hash) DO NOTHING;
""";
cmd.Parameters.AddWithValue("$id", id);
cmd.Parameters.AddWithValue("$hash", hash);
cmd.Parameters.AddWithValue("$data", data);
cmd.Parameters.AddWithValue("$w", pixelWidth);
cmd.Parameters.AddWithValue("$h", pixelHeight);
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT Id FROM Asset WHERE Hash = $hash;";
return (string)cmd.ExecuteScalar()!;
}
/// True when the asset already holds exactly these bytes. Used to decide whether a
/// bundled image needs re-seeding: comparing bytes cannot go stale the way a version constant
/// can (the constant and the embedded resource were built at different moments twice).
public bool AssetMatches(string assetId, byte[] data)
{
if (string.IsNullOrWhiteSpace(assetId)) return false;
var hash = Convert.ToHexString(SHA256.HashData(data));
using var cmd = _connection.CreateCommand();
cmd.CommandText = "SELECT 1 FROM Asset WHERE Id = $id AND Hash = $hash;";
cmd.Parameters.AddWithValue("$id", assetId);
cmd.Parameters.AddWithValue("$hash", hash);
return cmd.ExecuteScalar() != null;
}
// ─── License (TASK 10 — Polar billing) ───
/// The saved license key, or null if never entered.
}