<?php
/**
 * Demo Oracle — test de connexion (POST AJAX -> JSON).
 *
 * Modele : modules/postgresql/test_connection.php, et non celui du module
 * Oracle. Ce dernier consacre 260 de ses 312 lignes a l'extraction de
 * portefeuille et a l'analyse de tnsnames.ora, pour des modes de connexion que
 * la demonstration a retires (cf. l'en-tete de lib/DemoOracleOciConnect.php).
 * Ici il n'y a qu'un mode : la connexion directe host:port/service.
 *
 * Reponse : {ok, msg, banner, diag}
 *   diag = {diag_pack: bool, bash_snapper: bool, dba_role: bool}
 *
 * LE ROLE DBA EST RAPPORTE, JAMAIS BLOQUANT. Le module complet refuse la
 * connexion sans lui, parce qu'il en a besoin pour ses actions. La
 * demonstration n'ecrit rien, et son guide administrateur recommande
 * explicitement un compte en lecture seule : refuser ici contredirait sa
 * propre documentation.
 *
 * Ce fichier ne contient AUCUN appel OCI : la partie qui parle a Oracle vit
 * dans lib/DemoOracleConnectionTest.php. Dans ce module, aucun appel OCI ne
 * vit hors de lib/, et c'est ce qui rend verifiable l'affirmation « la
 * demonstration n'ecrit rien » — il suffit d'auditer un seul repertoire. Le
 * banc de test fait respecter la regle.
 *
 * Restent ici les responsabilites d'un point d'entree HTTP : les gardes, la
 * resolution des parametres, l'i18n et le JSON.
 */
require_once dirname(__DIR__, 2) . '/bootstrap.php';
require_once __DIR__ . '/lib/boot.php';
require_once __DIR__ . '/lib/DemoOracleAshSource.php';
require_once __DIR__ . '/lib/DemoOracleConnectionTest.php';

header('Content-Type: application/json; charset=UTF-8');

/** Reponse JSON et fin. */
$reply = function (bool $ok, string $msg, string $banner = '', ?array $diag = null): void {
    echo json_encode([
        'ok'     => $ok,
        'msg'    => $msg,
        'banner' => $banner,
        'diag'   => $diag,
    ]);
    exit;
};

/* ── Gardes ──────────────────────────────────────────────────────────────────
 *
 * L'endpoint entier est reserve aux managers, y compris pour tester une base
 * deja enregistree. Le module complet ne restreint que le test ad-hoc
 * (db_id = 0) ; la restriction est ici totale, pour deux raisons : seuls les
 * managers declarent des bases, et un point d'entree qui ouvre une connexion
 * TCP vers un hote fourni par le client fait du serveur web un connecteur
 * sortant — de quoi balayer un reseau interne depuis l'exterieur.
 */
if (!demo_oracle_is_manager($currentUser)
    || !$moduleRegistry->canAccessAsAdmin('demo_oracle', $currentUser)) {
    $reply(false, t('msg.access_denied'));
}
if (!hash_equals(Session::get('csrf_token', ''), $_POST['csrf_token'] ?? '')) {
    $reply(false, t('msg.error'));
}

/* ── Parametres ──────────────────────────────────────────────────────────── */
$dbId  = (int)($_POST['db_id'] ?? 0);
$type  = ($_POST['conn_type'] ?? 'standard') === 'cloud' ? 'cloud' : 'standard';
$host  = trim((string)($_POST['host']         ?? ''));
$port  = (int)($_POST['port']                 ?? 1521);
$svc   = trim((string)($_POST['service_name'] ?? ''));
$alias = trim((string)($_POST['tns_alias']    ?? ''));
$user  = trim((string)($_POST['username']     ?? ''));
$pass  = (string)($_POST['password']          ?? '');
$sysdba = !empty($_POST['sysdba']) && $_POST['sysdba'] !== '0';

/* Le portefeuille n'est jamais transmis par le formulaire : il est deja
   extrait sur le disque, et son chemin vient de la fiche. Tester une base
   cloud suppose donc qu'elle soit deja enregistree. */
$wallet = '';

/*
 * Champs vides et base deja enregistree : on complete depuis la fiche.
 *
 * Le formulaire suit la regle « laisser vide pour conserver » ; le test doit
 * s'y conformer, sinon verifier une base existante sans retaper son mot de
 * passe echouerait sur une authentification vide — et le message d'erreur
 * accuserait Oracle d'un probleme qui vient de l'interface.
 */
if ($dbId > 0) {
    $rec = $db->fetchOne(
        "SELECT conn_type, host, port, service_name, tns_alias, wallet_path,
                username, password, sysdba
           FROM demo_oracle_databases WHERE id = ?",
        [$dbId]
    );
    if (!$rec) {
        $reply(false, t('demo_oracle.db_not_found'));
    }
    if (!isset($_POST['conn_type'])) $type = ($rec['conn_type'] ?? 'standard') === 'cloud' ? 'cloud' : 'standard';
    if ($host  === '') $host  = (string)($rec['host'] ?? '');
    if ($port  === 0)  $port  = (int)($rec['port'] ?? 1521);
    if ($svc   === '') $svc   = (string)($rec['service_name'] ?? '');
    if ($alias === '') $alias = (string)($rec['tns_alias'] ?? '');
    if ($user  === '') $user  = (string)($rec['username'] ?? '');
    if (!isset($_POST['sysdba'])) $sysdba = !empty($rec['sysdba']);
    $wallet = (string)($rec['wallet_path'] ?? '');

    if ($pass === '') {
        /*
         * Crypto::decrypt leve une RuntimeException si l'extension sodium
         * manque. Non attrapee, elle produirait une erreur fatale PHP au
         * milieu d'une reponse JSON : le navigateur n'y verrait qu'une erreur
         * d'analyse, sans rapport avec la cause reelle.
         */
        try {
            $pass = Crypto::decrypt((string)($rec['password'] ?? '')) ?? '';
        } catch (Throwable $e) {
            $reply(false, t('demo_oracle.test_decrypt_failed'));
        }
    }
}

if ($port < 1 || $port > 65535) $port = 1521;

/* Une base cloud non encore enregistree n'a pas de portefeuille sur le
   disque : il n'y a rien a tester tant qu'elle n'est pas creee. */
if ($type === 'cloud' && $wallet === '') {
    $reply(false, t('demo_oracle.cloud_test_after_save'));
}
if (!function_exists('oci_connect')) {
    $reply(false, t('demo_oracle.test_oci8_missing'));
}

/* ── Connexion et diagnostic ─────────────────────────────────────────────── */
$res = DemoOracleConnectionTest::run([
    'conn_type'    => $type,
    'host'         => $host,
    'port'         => $port,
    'service_name' => $svc,
    'tns_alias'    => $alias,
    'wallet_path'  => $wallet,
], $user, $pass, $sysdba);

if (!$res['ok']) {
    $ora = $res['ora'];
    $reply(false, isset($ora['code'], $ora['message'])
        ? 'ORA-' . $ora['code'] . ': ' . trim($ora['message'])
        : t('demo_oracle.test_failed'));
}

$reply(true, t('demo_oracle.test_success'), $res['banner'], $res['diag']);
