-- Per-instance attribute training on an owned instance.
--
-- WHY A TABLE AND NOT COLUMNS. A training effect is (attribute slot, amount),
-- and a game may author one per slot. Six nullable columns would encode the
-- slot in the schema and force a migration to add a seventh; a row per slot
-- keeps the slot a value. It is also the smallest shape that lets the PRIMARY
-- KEY do the work described below.
--
-- WHY THE PRIMARY KEY IS (owned_card_id, attribute_index). Whether FIFA 17
-- REPLACES, STACKS, MERGES or REFUSES a second training on an attribute that
-- already carries one is UNKNOWN: no shipped table encodes it, and the client
-- holds no consumable-effect logic at all to reverse (no binary in the install
-- reads `fcc_trainingcards`, so effects are server-authoritative). Rather than
-- pick one of those behaviours and ship a guess as though it were recovered,
-- the key makes a second application to the SAME slot a constraint violation,
-- which the apply path turns into an explicit refusal that consumes nothing.
-- The unknown is therefore enforced by the schema instead of being papered over.
-- When the behaviour is proven, the change is a deliberate one-line relaxation
-- plus the arithmetic it implies — not an unpicking of accumulated bad state.
--
-- `attribute_index` is a slot in CORE's own six-attribute card model, in the
-- declaration order of `CardDefinition` (0 pace, 1 shooting, 2 passing,
-- 3 dribbling, 4 defending, 5 physical). It is deliberately NOT a FIFA
-- attribute name: mapping "GK speed" onto slot 4 is the FIFA 17 adapter's
-- reversed knowledge, and Core stays game-neutral by only ever indexing its own
-- model. The CHECK pins the slot to that model's width.
--
-- `amount` is bounded at 99 because it is added to an attribute whose domain is
-- 1..=99; a larger stored value could not mean anything. The tighter, per-game
-- ceiling (FIFA 17 authors only 5/10/15) is validated at apply time, where the
-- game's table is in scope, not here.
--
-- ON DELETE CASCADE is load-bearing: the pool enables `foreign_keys`
-- (`db.rs:20`), so quick-selling or otherwise destroying a trained instance
-- takes its training with it and cannot leave a row pointing at a dead item.
CREATE TABLE owned_card_training (
    owned_card_id   TEXT    NOT NULL REFERENCES owned_cards(id) ON DELETE CASCADE,
    attribute_index INTEGER NOT NULL CHECK (attribute_index BETWEEN 0 AND 5),
    amount          INTEGER NOT NULL CHECK (amount >= 1 AND amount <= 99),
    -- The definition that granted it, kept for audit and for the eventual
    -- lifecycle work; Core never interprets it.
    source_card_id  TEXT    NOT NULL,
    applied_at      TEXT    NOT NULL,
    PRIMARY KEY (owned_card_id, attribute_index)
);

-- The projection reads every effect for a set of instances on each /collection
-- call, so the lookup is by instance.
CREATE INDEX idx_owned_card_training_owned ON owned_card_training(owned_card_id);
