-- Update item_act to noop on negative or zero sats
CREATE OR REPLACE FUNCTION item_act(item_id INTEGER, user_id INTEGER, act "ItemActType", act_sats INTEGER)
RETURNS INTEGER
LANGUAGE plpgsql
AS $$
DECLARE
    user_msats BIGINT;
    act_msats BIGINT;
    fee_msats BIGINT;
    item_act_id INTEGER;
    fwd_entry record; -- for loop iterator variable to iterate across forward recipients
    fwd_msats BIGINT; -- for loop variable calculating how many msats to give each forward recipient
    total_fwd_msats BIGINT := 0; -- accumulator to see how many msats have been forwarded for the act
BEGIN
    PERFORM ASSERT_SERIALIZED();

    IF act_sats <= 0 THEN
        RETURN 0;
    END IF;

    act_msats := act_sats * 1000;
    SELECT msats INTO user_msats FROM users WHERE id = user_id;
    IF act_msats > user_msats THEN
        RAISE EXCEPTION 'SN_INSUFFICIENT_FUNDS';
    END IF;

    -- deduct msats from actor
    UPDATE users SET msats = msats - act_msats WHERE id = user_id;

    IF act = 'TIP' THEN
        -- call to influence weightedVotes ... we need to do this before we record the acts because
        -- the priors acts are taken into account
        PERFORM weighted_votes_after_tip(item_id, user_id, act_sats);
        -- call to denormalize sats and commentSats
        PERFORM sats_after_tip(item_id, user_id, act_msats);

        -- take 10% and insert as FEE
        fee_msats := CEIL(act_msats * 0.1);
        act_msats := act_msats - fee_msats;

        -- save the fee act into item_act_id so we can record referral acts
        INSERT INTO "ItemAct" (msats, "itemId", "userId", act, created_at, updated_at)
            VALUES (fee_msats, item_id, user_id, 'FEE', now_utc(), now_utc())
            RETURNING id INTO item_act_id;

        -- leave the rest as a tip
        INSERT INTO "ItemAct" (msats, "itemId", "userId", act, created_at, updated_at)
            VALUES (act_msats, item_id, user_id, 'TIP', now_utc(), now_utc());

        -- denormalize bounty paid (if applicable)
        PERFORM bounty_paid_after_act(item_id, user_id);

        -- add sats to actees' balance and stacked count
        FOR fwd_entry IN SELECT "userId", "pct" FROM "ItemForward" WHERE "itemId" = item_id
        LOOP
            -- fwd_msats represents the sats for this forward recipient from this particular tip action
            fwd_msats := act_msats * fwd_entry.pct / 100;
            -- keep track of how many msats have been forwarded, so we can give any remaining to OP
            total_fwd_msats := fwd_msats + total_fwd_msats;

            UPDATE users
            SET msats = msats + fwd_msats, "stackedMsats" = "stackedMsats" + fwd_msats
            WHERE id = fwd_entry."userId";
        END LOOP;

        -- Give OP any remaining msats after forwards have been applied
        IF act_msats - total_fwd_msats > 0 THEN
            UPDATE users
            SET msats = msats + act_msats - total_fwd_msats, "stackedMsats" = "stackedMsats" + act_msats - total_fwd_msats
            WHERE id = (SELECT "userId" FROM "Item" WHERE id = item_id);
        END IF;
    ELSE -- BOOST, POLL, DONT_LIKE_THIS, STREAM
        -- call to influence if DONT_LIKE_THIS weightedDownVotes
        IF act = 'DONT_LIKE_THIS' THEN
            PERFORM weighted_downvotes_after_act(item_id, user_id, act_sats);
        END IF;

        INSERT INTO "ItemAct" (msats, "itemId", "userId", act, created_at, updated_at)
            VALUES (act_msats, item_id, user_id, act, now_utc(), now_utc())
            RETURNING id INTO item_act_id;
    END IF;

    -- store referral effects
    PERFORM referral_act(item_act_id);

    RETURN 0;
END;
$$;