-- Migration 016: Multi-device session tracking
-- Progetto allineamento NIS2 ↔ TRPG — Fase 2 / G05
-- Data: 2026-05-29
-- Mutuata da TRPG /var/www/trpg-agile/sql/078_active_sessions.sql
--
-- Crea tabella `active_sessions` per tracciare ogni sessione JWT come riga
-- distinta (chiave = jti del token), con device label + IP + last activity +
-- revoke audit. Permette:
--   - lista sessioni attive per utente (Settings → Sicurezza)
--   - revoca selettiva ("esci da questo dispositivo")
--   - revoca cascade su password change
--   - context switch tra organization (Fase 3) senza rotazione manuale
--
-- Differenze rispetto a TRPG:
--   - aggiunta colonna `organization_id` (NIS2 ha tenant esplicito X-Organization-Id)
--   - ENUM revoked_reason include già 'context_switch' (TRPG l'ha aggiunta in
--     migration 093; noi nasciamo con essa per evitare doppia migration)
--
-- Anche aggiungiamo `session_jti` a `refresh_tokens` per legare
-- ogni refresh alla sua sessione (rotazione safe).
--
-- Rollback:
--   ALTER TABLE refresh_tokens DROP INDEX idx_refresh_jti, DROP COLUMN session_jti;
--   DROP TABLE IF EXISTS active_sessions;

SET @tbl := (
    SELECT COUNT(*) FROM information_schema.TABLES
    WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'active_sessions'
);
SET @sql_tbl := IF(@tbl = 0,
    'CREATE TABLE active_sessions (
        id CHAR(32) NOT NULL PRIMARY KEY COMMENT ''jti del JWT (bin2hex(random_bytes(16)))'',
        user_id INT NOT NULL,
        organization_id INT NULL COMMENT ''org attiva al momento del login'',
        ip_address VARCHAR(45) NOT NULL,
        user_agent VARCHAR(512) NULL,
        device_label VARCHAR(120) NULL COMMENT ''Parsing UA-friendly (es. Chrome/Win11)'',
        created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT ''login time'',
        last_activity_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
        expires_at TIMESTAMP NOT NULL COMMENT ''Hard cap = login + refresh TTL'',
        revoked_at TIMESTAMP NULL DEFAULT NULL,
        revoked_reason ENUM(''logout'',''force'',''password_change'',''admin'',''expired_idle'',''context_switch'') NULL,
        FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
        INDEX idx_user_active (user_id, revoked_at, last_activity_at),
        INDEX idx_last_activity (last_activity_at),
        INDEX idx_expires (expires_at)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
      COMMENT=''JWT session tracking — solo utenti umani (API key B2B escluse)''',
    'SELECT ''active_sessions già presente — skip'' AS info'
);
PREPARE stmt FROM @sql_tbl; EXECUTE stmt; DEALLOCATE PREPARE stmt;

-- Aggiungi session_jti a refresh_tokens
SET @col_jti := (
    SELECT COUNT(*) FROM information_schema.COLUMNS
    WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'refresh_tokens' AND COLUMN_NAME = 'session_jti'
);
SET @sql_col := IF(@col_jti = 0,
    'ALTER TABLE refresh_tokens ADD COLUMN session_jti CHAR(32) NULL COMMENT ''FK logica → active_sessions.id'' AFTER user_id',
    'SELECT ''refresh_tokens.session_jti già presente — skip'' AS info'
);
PREPARE stmt FROM @sql_col; EXECUTE stmt; DEALLOCATE PREPARE stmt;

SET @idx_jti := (
    SELECT COUNT(*) FROM information_schema.STATISTICS
    WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'refresh_tokens' AND INDEX_NAME = 'idx_refresh_jti'
);
SET @sql_idx := IF(@idx_jti = 0,
    'CREATE INDEX idx_refresh_jti ON refresh_tokens (session_jti)',
    'SELECT ''idx_refresh_jti già presente — skip'' AS info'
);
PREPARE stmt FROM @sql_idx; EXECUTE stmt; DEALLOCATE PREPARE stmt;

-- Verifica finale
SELECT TABLE_NAME, ENGINE, TABLE_COMMENT
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'active_sessions';

SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_COMMENT
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'refresh_tokens' AND COLUMN_NAME = 'session_jti';
