<?php
/**
 * Demo Oracle — test de connexion : la partie qui parle a Oracle.
 *
 * Cette classe existe pour une raison precise, et le banc de test la fait
 * respecter : dans ce module, AUCUN appel OCI ne vit hors de lib/. C'est ce
 * qui rend verifiable l'affirmation « la demonstration n'ecrit rien » — il
 * suffit d'auditer un seul repertoire. Ecrire le oci_connect directement dans
 * test_connection.php aurait ouvert une exception a cette regle, et une
 * exception suffit a la rendre inverifiable.
 *
 * test_connection.php ne garde donc que ce qui est du ressort d'un point
 * d'entree HTTP : les gardes, la resolution des parametres, l'i18n et le JSON.
 *
 * Trois lectures, et rien d'autre : la banniere, la licence Diagnostic Pack
 * (deleguee a DemoOracleAshSource), la presence du role DBA.
 *
 * Safe to include multiple times (guard sur class_exists).
 */
require_once __DIR__ . '/DemoOracleDsn.php';

if (class_exists('DemoOracleConnectionTest')) return;

class DemoOracleConnectionTest
{
    /**
     * Ouvre une connexion directe, releve la banniere et le diagnostic, ferme.
     *
     * Ne leve pas : un echec est une valeur de retour, comme partout ailleurs
     * dans le module. La page appelante decide de ce qu'elle en dit.
     *
     * @param array $rec conn_type, host, port, service_name, tns_alias, wallet_path
     * @return array{ok: bool, ora: ?array, banner: string, diag: array}
     *         `ora` porte l'erreur OCI brute (code, message) en cas d'echec.
     */
    public static function run(array $rec, string $user, string $pass, bool $sysdba): array
    {
        $out = ['ok' => false, 'ora' => null, 'banner' => '', 'diag' => []];

        /* La meme construction que la connexion des rapports : tester une
           chaine differente de celle qui servira ensuite n'aurait rien
           prouve. Elle couvre le direct comme le portefeuille. */
        $built = DemoOracleDsn::build($rec);
        if (!$built['ok']) {
            $out['ora'] = ['code' => 0, 'message' => $built['msg']];
            return $out;
        }

        $conn = @oci_connect(
            $user, $pass, $built['dsn'], 'AL32UTF8',
            $sysdba ? OCI_SYSDBA : OCI_DEFAULT
        );
        if (!$conn) {
            $err = oci_error();
            $out['ora'] = is_array($err) ? $err : null;
            return $out;
        }

        $out['ok']     = true;
        $out['banner'] = self::banner($conn);
        $out['diag']   = [
            // Ces deux-la decident deja, en production, si les rapports temps
            // reel et historique ont une source ASH. Les montrer ici repond a
            // la vraie question : ce compte fera-t-il tourner la demonstration ?
            /* Une base Autonomous embarque le Diagnostic Pack : le
               parametre d'instance ne le dit pas toujours, la regle du module
               complet est reprise telle quelle. */
            'diag_pack'    => ($rec['conn_type'] ?? '') === 'cloud'
                              || DemoOracleAshSource::hasDiagnosticPack($conn),
            'bash_snapper' => DemoOracleAshSource::hasBashSnapper($conn),
            // Rapporte, jamais bloquant : la demonstration n'ecrit rien et son
            // guide administrateur recommande un compte en lecture seule.
            'dba_role'     => $sysdba || self::hasDbaRole($conn),
        ];

        oci_close($conn);
        return $out;
    }

    /** Premiere ligne de V$VERSION, ou chaine vide. */
    private static function banner($conn): string
    {
        $stm = @oci_parse($conn, "SELECT banner FROM v\$version WHERE rownum = 1");
        if (!$stm) return '';

        $banner = '';
        if (@oci_execute($stm, OCI_NO_AUTO_COMMIT) && ($r = oci_fetch_assoc($stm))) {
            $banner = (string)($r['BANNER'] ?? '');
        }
        oci_free_statement($stm);
        return $banner;
    }

    /** Le role DBA est-il actif dans la session courante ? */
    private static function hasDbaRole($conn): bool
    {
        $stm = @oci_parse($conn, "SELECT 1 FROM session_roles WHERE role = 'DBA'");
        if (!$stm) return false;

        $has = false;
        if (@oci_execute($stm, OCI_NO_AUTO_COMMIT)) {
            $has = (bool)oci_fetch_array($stm);
        }
        oci_free_statement($stm);
        return $has;
    }
}
