-- Alter the activation_codes table to accept a 9 digit string for card_id instead of a UUID
ALTER TABLE activation_codes ALTER COLUMN card_id TYPE VARCHAR(9);

-- Alter the activation_logs table to accept a 9 digit string for card_id instead of a UUID
ALTER TABLE activation_logs ALTER COLUMN card_id TYPE VARCHAR(9);

-- Drop the old procedure which accepted UUID
DROP FUNCTION IF EXISTS activate_payment_card(UUID, TEXT, INET, JSONB);

-- Recreate it to accept VARCHAR(9)
CREATE OR REPLACE FUNCTION activate_payment_card(
    p_card_id VARCHAR(9),     -- 9 digit string de la tarjeta
    p_input_code TEXT,  -- El código que mete el usuario, puede ser incorrecto en este punto
    p_ip_address INET,  -- Datos de origen de la activación 
    p_device_info JSONB -- Datos del dispositivo de origen
)
RETURNS TABLE (
    success BOOLEAN,
    message TEXT
) AS $$
DECLARE
    v_code_hash TEXT;
    v_is_used BOOLEAN;
    v_is_blocked BOOLEAN;
    v_expires_at TIMESTAMPTZ;
    v_failed_attempts INT;
    v_max_attempts CONSTANT INT := 3;
BEGIN
    -- 1. Obtener datos del código y bloquear la fila para actualización (FOR UPDATE)
    -- sería raro que 2 clientes intentasen modificar la tarjeta a la vez.  
    SELECT code_hash, is_used, is_blocked, expires_at, failed_attempts
    INTO v_code_hash, v_is_used, v_is_blocked, v_expires_at, v_failed_attempts
    FROM activation_codes
    WHERE card_id = p_card_id
    FOR UPDATE;

    -- 2. Validaciones:
    -- 2.1 Si no existe ninguna tarjeta con ese card_id 
    IF NOT FOUND THEN
        RETURN QUERY SELECT FALSE, 'CARD_NOT_FOUND';
        RETURN;
    END IF;
    
    -- 2.2 Si el código introducido ya ha sido usado
    IF v_is_used THEN
        RETURN QUERY SELECT FALSE, 'ALREADY_ACTIVATED';
        RETURN;
    END IF;
        
    -- 2.3 Si el código ya ha sido bloqueado o se ha intentado demasiadas veces
    IF v_is_blocked OR v_failed_attempts >= v_max_attempts THEN
        RETURN QUERY SELECT FALSE, 'CODE_BLOCKED';
        RETURN;
    END IF;
    
    -- 2.4 Si el código es demasiado viejo
    IF v_expires_at < NOW() THEN
        RETURN QUERY SELECT FALSE, 'CODE_EXPIRED';
        RETURN;
    END IF;

    -- 3. Verificación del Hash del codigo que ha introducido el usuario 
    -- En la bdd guardo el hash, al procedimiento se introduce el codigo per-se 
    -- Si el código NO coincide:
    IF v_code_hash != crypt(p_input_code, v_code_hash) THEN
        UPDATE activation_codes
        -- 3.1 Control de intentos
        SET failed_attempts = failed_attempts + 1,
            is_blocked = (failed_attempts + 1 >= v_max_attempts)
        WHERE card_id = p_card_id;
        -- 3.2 Se loguea el fallo
        INSERT INTO activation_logs (card_id, action_type, ip_address, device_info)
        VALUES (p_card_id, 'FAILED_ATTEMPT', p_ip_address, p_device_info);

        RETURN QUERY SELECT FALSE, 'INVALID_CODE';
        RETURN;
    END IF;

    -- 4. Si el código ES correcto:
    -- Marcar código como usado
    UPDATE activation_codes
    SET is_used = TRUE
    WHERE card_id = p_card_id;

    -- Activar la tarjeta
    UPDATE payment_cards
    SET status = 'ACTIVE',
        activated_at = NOW()
    WHERE card_id = p_card_id;

    -- Registrar éxito en auditoría
    INSERT INTO activation_logs (card_id, action_type, ip_address, device_info)
    VALUES (p_card_id, 'ACTIVATION_SUCCESS', p_ip_address, p_device_info);

    RETURN QUERY SELECT TRUE, 'SUCCESS';

EXCEPTION WHEN OTHERS THEN
    -- En caso de error inesperado, Postgres hace rollback automático
    RETURN QUERY SELECT FALSE, 'INTERNAL_ERROR: ' || SQLERRM;
END;
$$ LANGUAGE plpgsql;
