--! Previous: sha1:f03c81c8d0ca3976d4b6fe4792ee7558d1c2c2f1
--! Hash: sha1:c8b959244b460096124e37cb9014d1a1b3faf7d3
--! Message: pull command gets units

--- Runs the full /pull command.
--- Error codes:
---     VGBCG: Bad channel (game). This is not a valid channel to send game commands in.
---     VGBGG: Bad guild (game). This is not a valid guild to send game commands in.
---     VGNYJ: Not yet joined. The Discord user using has not joined the game yet.
---     VGNEC: Not enough currency.
---     VGNUT: No units in tier. The randomly selected tier has no units and it's impossible to summon a unit.
---     VGUNF: Unit not found. The randomly selected unit was not found.
---     VGTNF: Tier not found. The randomly selected tier was not found.
CREATE OR REPLACE FUNCTION Command_Pull(
    IN requestedChannel DiscordChannel.discordId%TYPE,
    IN requestedGuild DiscordChannel.guildId%TYPE,
    IN forId DiscordUser.discordId%TYPE,
    IN newUsername DiscordUser.username%TYPE,
    IN newDiscriminator DiscordUser.discriminator%TYPE,
    IN count INT
)
    RETURNS TABLE
            (
                summonedUnitInstanceId SummonedUnit.instanceId%TYPE,
                summonedUnitId         Unit.id%TYPE,
                summonedUnitName       Unit.name%TYPE,
                summonedUnitSubtitle   Unit.subtitle%TYPE,
                summonedUnitTierId     UnitTier.id%TYPE,
                summonedUnitTierName   UnitTier.name%TYPE,
                summonCost             Player.currency%TYPE,
                resultingCurrency      Player.currency%TYPE,
                firstTimePull          BOOLEAN,
                wasAlreadySummoned     BOOLEAN
            )
    STRICT
    VOLATILE
    ROWS 10
AS
$$
DECLARE
    costPerSummon CONSTANT INT       := 10;
    playerId               Player.id%TYPE;
    cost                   Player.currency%TYPE;
    oldCurrency            Player.currency%TYPE;
    playerLastDaily        Player.lastDaily%TYPE;
    tierRandom             FLOAT;
    validUnits             INTEGER[] := ARRAY []::INTEGER[];
    unitRandom             FLOAT;
BEGIN
    CALL CheckGameCommandIn(requestedChannel, requestedGuild);
    SELECT InvokingPlayer.id, InvokingPlayer.currency, InvokingPlayer.lastDaily
    INTO playerId, oldCurrency, playerLastDaily
    FROM GetInvokingPlayer(forId, newUsername, newDiscriminator) AS InvokingPlayer;
    IF playerId IS NULL THEN
        RAISE EXCEPTION USING
            ERRCODE = 'VGNYJ',
            MESSAGE = 'Not yet joined',
            DETAIL = 'You haven''t joined the game yet, and can''t use this command until you do.',
            HINT = 'Use the /join command to join the game!';
    END IF;
    cost = costPerSummon * count;
    UPDATE Player SET currency = currency - cost WHERE id = playerId AND currency >= cost;
    IF NOT FOUND THEN
        RAISE EXCEPTION USING
            ERRCODE = 'VGNEC',
            MESSAGE = 'Not enough currency',
            DETAIL = format('Pulling %s heroines would cost %s currency, but you only have %s currency.',
                            count, cost, oldCurrency),
            HINT = CASE NextDailyCommand(playerLastDaily) > NOW()
                       WHEN FALSE THEN 'You can use the /daily command to get some more currency for today!'
                       ELSE format('You can use the /daily command again <t:%s:R> to get some more currency!',
                                   extract(EPOCH FROM NextDailyCommand(playerLastDaily))::INT)
                END;
    END IF;
    FOR summonIdx IN 1..count
        LOOP
            tierRandom = random();
            unitRandom = random();
            summonedUnitTierId = id
                                 FROM UnitTier
                                 WHERE (UnitTier.pullWeightLower <= tierRandom AND
                                        UnitTier.pullWeightUpper > tierRandom);
            validUnits = ARRAY(SELECT id FROM Unit WHERE (Unit.tierId = summonedUnitTierId));
            IF validUnits = ARRAY []::INTEGER[] THEN
                RAISE EXCEPTION USING
                    ERRCODE = 'VGNUT',
                    MESSAGE = 'No units of tier',
                    DETAIL = format(
                            'The dice selected the tier with ID ''%s'', but there are no units of that tier!',
                            summonedUnitTierId),
                    HINT = 'This indicates a problem in the database. '
                        || 'Contact an admin to fix this problem and get your justly deserved pull.';
            END IF;
            summonedUnitId = validUnits[
                    floor(unitRandom::DOUBLE PRECISION * array_length(validUnits, 1)::DOUBLE PRECISION)::INTEGER + 1];
            SELECT Unit.id, Unit.name, Unit.subtitle, Unit.tierId
            INTO summonedUnitId, summonedUnitName, summonedUnitSubtitle, summonedUnitTierId
            FROM Unit
            WHERE Unit.id = summonedUnitId;
            IF NOT FOUND THEN
                RAISE EXCEPTION USING
                    ERRCODE = 'VGUNF',
                    MESSAGE = 'Unit not found',
                    DETAIL = format(
                            'The dice selected the unit with ID ''%s'' of tier ID ''%s'', but the unit wasn''t found!',
                            summonedUnitId, summonedUnitTierId),
                    HINT = 'This indicates a problem in the database.'
                        || ' Contact an admin to fix this problem and get your justly deserved pull.';
            END IF;
            SELECT UnitTier.id, UnitTier.name
            INTO summonedUnitTierId, summonedUnitTierName
            FROM UnitTier
            WHERE UnitTier.id = summonedUnitTierId;
            IF NOT FOUND THEN
                RAISE EXCEPTION USING
                    ERRCODE = 'VGTNF',
                    MESSAGE = 'Tier not found',
                    DETAIL = format(
                            'The dice selected the unit with ID ''%s'' of tier ID ''%s'', but the tier wasn''t found!',
                            summonedUnitId, summonedUnitTierId),
                    HINT = 'This indicates a problem in the database.'
                        || ' Contact an admin to fix this problem and get your justly deserved pull.';
            END IF;
            summonedUnitInstanceId = NULL::INTEGER;
            summonCost = costPerSummon;
            resultingCurrency = oldCurrency - (costPerSummon * summonIdx);
            firstTimePull = NULL::BOOLEAN;
            wasAlreadySummoned = NULL::BOOLEAN;
            RETURN NEXT;
        END LOOP;
END;

$$ LANGUAGE 'plpgsql';
