<?php
/**
 * Demo Oracle — choix de la source ASH.
 *
 * Le module complet delegue ce choix a OracleBashSnapperRepository, 1700 lignes
 * qui savent aussi DEPLOYER, demarrer, arreter et purger le paquetage
 * SW$BASH_SNAPPER. La demonstration n'a besoin que de la moitie lecture : elle
 * peut UTILISER un paquetage deja installe, elle ne sait pas en installer un.
 * Le reste n'est donc pas repris.
 *
 * Trois cas, dans cet ordre :
 *   1. Diagnostic Pack sous licence, ou base Autonomous (qui l'embarque)
 *                                    -> GV$ACTIVE_SESSION_HISTORY
 *   2. sinon, SW$BASH_* installe     -> SW$BASH_ACTIVE_SESSION_HISTORY
 *   3. sinon                         -> aucune source, la page le dit
 *
 * Safe to include multiple times (guard sur class_exists).
 */
if (class_exists('DemoOracleAshSource')) return;

class DemoOracleAshSource
{
    /** Vue ASH du Diagnostic Pack. */
    public const VIEW_DIAG = 'gv$active_session_history';

    /** Vue miroir posee par sw$bash_snapper (memes noms de colonnes). */
    public const VIEW_BASH = 'SW$BASH_ACTIVE_SESSION_HISTORY';

    /**
     * Le Diagnostic Pack est-il utilisable sur cette base ?
     *
     * Meme regle que lib/OraclePackLicense.php du module complet : une base
     * Autonomous embarque le pack, sinon on lit
     * CONTROL_MANAGEMENT_PACK_ACCESS.
     */
    public static function hasDiagnosticPack($conn, bool $isCloud = false): bool
    {
        /* Une base Autonomous embarque le Diagnostic Pack, que le parametre
           d'instance l'annonce ou non. Meme regle que le module complet
           (lib/OraclePackLicense.php).

           A RETENIR : dans ce cas le parametre n'est PAS lu du tout. Une base
           declaree en connexion cloud est reputee licenciee, et les lectures
           AWR partent meme si CONTROL_MANAGEMENT_PACK_ACCESS vaut NONE. Les
           pages doivent le dire — cf. packAccess() et $licenseFrom. */
        if ($isCloud) return true;

        return stripos(self::packAccess($conn), 'DIAGNOSTIC') !== false;
    }

    /**
     * La valeur brute de CONTROL_MANAGEMENT_PACK_ACCESS.
     *
     * Publique parce que les pages doivent pouvoir AFFICHER sur quelle base
     * la licence a ete decidee : sans cela, impossible de distinguer a
     * l'ecran « le parametre autorise » de « la base est declaree cloud, le
     * parametre n'a pas ete consulte ».
     *
     * TROIS reponses possibles, et il faut les distinguer :
     *
     *   'DIAGNOSTIC+TUNING'  la valeur lue
     *   'NONE'               la valeur lue, qui vaut NONE
     *   ''                   la vue est ILLISIBLE depuis ce compte
     *
     * Le troisieme cas est reel : un compte dedie au collecteur — SWBS_OWNER,
     * par exemple — n'a pas forcement SELECT sur V$PARAMETER, et la requete
     * tombe en ORA-00942. Rendre 'NONE' dans ce cas ferait AFFIRMER a l'ecran
     * que le pack est desactive, alors qu'on n'en sait rien.
     *
     * Les deux cas restent fail-closed pour la decision : stripos('', ...)
     * est faux, donc pas de licence presumee. Seul l'AFFICHAGE change.
     */
    public static function packAccess($conn): string
    {
        $stm = @oci_parse($conn,
            "SELECT value FROM V\$PARAMETER WHERE name = 'control_management_pack_access'");
        if (!$stm) return '';

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

    /**
     * Le Tuning Pack est-il utilisable ? Conditionne l'affichage du SQL Monitor
     * dans le rapport des sessions actives.
     */
    public static function hasTuningPack($conn, bool $isCloud = false): bool
    {
        if ($isCloud) return true;

        return stripos(self::packAccess($conn), 'TUNING') !== false;
    }

    /**
     * Le paquetage sw$bash_snapper est-il installe et alimente ?
     *
     * Reprend la detection du module complet : le corps du paquetage doit etre
     * VALID quelque part, et les trois tables de base presentes chez le meme
     * proprietaire. Un paquetage sans ses tables ne sert a rien.
     */
    public static function hasBashSnapper($conn): bool
    {
        $schema = null;
        $stm = @oci_parse($conn,
            "SELECT owner FROM ALL_OBJECTS
              WHERE object_name = 'SW\$BASH_SNAPPER'
                AND object_type = 'PACKAGE BODY'
                AND status      = 'VALID'
              FETCH FIRST 1 ROW ONLY");
        if ($stm) {
            if (@oci_execute($stm, OCI_NO_AUTO_COMMIT) && ($r = oci_fetch_assoc($stm))) {
                $schema = (string)$r['OWNER'];
            }
            oci_free_statement($stm);
        }
        if ($schema === null) return false;

        $stm = @oci_parse($conn,
            "SELECT COUNT(*) cnt FROM ALL_TABLES
              WHERE owner = :o
                AND table_name IN ('SW\$BASH_ACTIVE_SESSION_HISTORY',
                                   'SW\$BASH_HIST_ACTIVE_SESS_HISTORY',
                                   'SW\$BASH_SETTINGS')");
        if (!$stm) return false;

        $ok = false;
        oci_bind_by_name($stm, ':o', $schema, 128, SQLT_CHR);
        if (@oci_execute($stm, OCI_NO_AUTO_COMMIT) && ($r = oci_fetch_assoc($stm))) {
            $ok = ((int)($r['CNT'] ?? 0) >= 3);
        }
        oci_free_statement($stm);
        return $ok;
    }

    /**
     * Resout la source a interroger.
     *
     * @param bool $isCloud la base est-elle joignable par portefeuille ?
     * @return array{view: string, source: string}
     *         `source` vaut 'diag_pack', 'bash_snapper' ou '' (aucune source).
     */
    public static function resolve($conn, bool $isCloud = false): array
    {
        if (self::hasDiagnosticPack($conn, $isCloud)) {
            return ['view' => self::VIEW_DIAG, 'source' => 'diag_pack'];
        }
        if (self::hasBashSnapper($conn)) {
            return ['view' => self::VIEW_BASH, 'source' => 'bash_snapper'];
        }
        return ['view' => '', 'source' => ''];
    }
}
