-- Durable idempotency for applying a consumable to a target.
--
-- Same discipline as `match_completions` (migration 0022): ONE effect per
-- (profile_id, action_identity). A sequential replay, a restart replay, a
-- concurrent duplicate, or a retried HTTP request all collide on this UNIQUE and
-- are refused BEFORE the target is mutated and BEFORE the source is consumed —
-- so a consumable can never be spent twice, and its effect can never be applied
-- twice from one spend.
--
-- `action_identity` is opaque to Core: the game adapter/host derives a stable
-- per-application token from its own wire request. Core never parses it.
--
-- `source_owned_card_id` / `target_owned_card_id` are deliberately NOT foreign
-- keys: the source row is DELETEd (or decremented to zero and deleted) by the
-- very transaction that writes this record, and the target may later be sold.
-- This table is an audit + replay record, not an ownership reference.
--
-- `effect` is the caller-supplied outcome summary stored verbatim as JSON text.
-- Core defines NO per-category formula: what a given consumable does to its
-- target is the calling game adapter's reversed behaviour, and an unreversed
-- behaviour must not be invented here.
CREATE TABLE consumable_applications (
    id                    TEXT PRIMARY KEY NOT NULL,
    profile_id            TEXT NOT NULL REFERENCES profiles(id),
    action_identity       TEXT NOT NULL,
    source_owned_card_id  TEXT NOT NULL,
    source_card_id        TEXT NOT NULL,
    source_content_kind   TEXT NOT NULL,
    -- 1 = the source instance was destroyed; 0 = a stack was decremented.
    source_consumed       INTEGER NOT NULL,
    -- Remaining stack size after a decrement, NULL when the instance was destroyed.
    source_quantity_after INTEGER,
    target_owned_card_id  TEXT,
    effect                TEXT NOT NULL,
    applied_at            TEXT NOT NULL,
    UNIQUE(profile_id, action_identity)
);

CREATE INDEX idx_consumable_applications_profile
    ON consumable_applications(profile_id);
