namespace NeonSprawl.Server.Game.Gathering;

/// <summary>PostgreSQL-backed world node instance capacity keyed by interactable id (NEO-61).</summary>
public sealed class PostgresResourceNodeInstanceStore(Npgsql.NpgsqlDataSource dataSource) : IResourceNodeInstanceStore
{
    /// <inheritdoc />
    public bool TryGetRemainingGathers(string interactableId, out ResourceNodeInstanceSnapshot snapshot)
    {
        var key = ResourceNodeInstanceIds.Normalize(interactableId);
        if (key.Length == 0)
        {
            snapshot = default;
            return false;
        }

        PostgresResourceNodeInstanceBootstrap.EnsureSchema(dataSource);
        using var conn = dataSource.OpenConnection();
        using var cmd = new Npgsql.NpgsqlCommand(
            "SELECT remaining_gathers FROM resource_node_instance WHERE interactable_id = @id;",
            conn);
        cmd.Parameters.AddWithValue("id", key);
        var scalar = cmd.ExecuteScalar();
        if (scalar is null or DBNull)
        {
            snapshot = default;
            return false;
        }

        var remaining = Convert.ToInt32(scalar);
        snapshot = new ResourceNodeInstanceSnapshot(key, remaining);
        return true;
    }

    /// <inheritdoc />
    public bool TryEnsureInitialized(string interactableId, int initialRemainingGathers)
    {
        var key = ResourceNodeInstanceIds.Normalize(interactableId);
        if (key.Length == 0 || initialRemainingGathers < 0)
        {
            return false;
        }

        PostgresResourceNodeInstanceBootstrap.EnsureSchema(dataSource);
        using var conn = dataSource.OpenConnection();
        using var cmd = new Npgsql.NpgsqlCommand(
            """
            INSERT INTO resource_node_instance (interactable_id, remaining_gathers, updated_at)
            VALUES (@id, @remaining, now())
            ON CONFLICT (interactable_id) DO NOTHING;
            """,
            conn);
        cmd.Parameters.AddWithValue("id", key);
        cmd.Parameters.AddWithValue("remaining", initialRemainingGathers);
        cmd.ExecuteNonQuery();
        return true;
    }

    /// <inheritdoc />
    public bool TryDecrementRemainingGathers(
        string interactableId,
        out int previousRemaining,
        out int remainingGathers)
    {
        previousRemaining = 0;
        remainingGathers = 0;
        var key = ResourceNodeInstanceIds.Normalize(interactableId);
        if (key.Length == 0)
        {
            return false;
        }

        PostgresResourceNodeInstanceBootstrap.EnsureSchema(dataSource);
        using var conn = dataSource.OpenConnection();
        using var cmd = new Npgsql.NpgsqlCommand(
            """
            UPDATE resource_node_instance
            SET remaining_gathers = remaining_gathers - 1,
                updated_at = now()
            WHERE interactable_id = @id
              AND remaining_gathers > 0
            RETURNING remaining_gathers + 1 AS previous_remaining, remaining_gathers;
            """,
            conn);
        cmd.Parameters.AddWithValue("id", key);
        using var reader = cmd.ExecuteReader();
        if (!reader.Read())
        {
            if (TryGetRemainingGathers(key, out var depleted) && depleted.RemainingGathers == 0)
            {
                remainingGathers = 0;
            }

            return false;
        }

        previousRemaining = reader.GetInt32(0);
        remainingGathers = reader.GetInt32(1);
        return true;
    }
}
