<?php
/**
 * Demo Oracle — construction du DSN, pour les deux types de connexion.
 *
 * Un seul endroit decide de la chaine de connexion. Le module complet la
 * reconstruit dans trois fichiers — databases.php pour son test a
 * l'enregistrement, OracleOciConnect pour les rapports, test_connection.php
 * pour le bouton — et ces trois copies ont diverge : celle de databases.php
 * passe l'alias TNS brut la ou les deux autres resolvent le descripteur
 * complet. Ici, la connexion et le test appellent la meme methode.
 *
 * DEUX TYPES
 *
 *   standard  host:port/service_name, rien d'autre a preparer.
 *   cloud     un alias TNS resolu depuis le tnsnames.ora du portefeuille. Le
 *             descripteur complet est reconstitue et MY_WALLET_DIRECTORY y est
 *             injecte, ce qui evite de dependre d'un TNS_ADMIN global : deux
 *             bases cloud declarees cote a cote ont chacune son portefeuille.
 *
 * TNS_ADMIN est neanmoins pose en secours, pour le cas ou le descripteur
 * n'aurait pas pu etre reconstitue et ou l'alias part tel quel a OCI8.
 *
 * Safe to include multiple times (guard sur class_exists).
 */
if (class_exists('DemoOracleDsn')) return;

class DemoOracleDsn
{
    /**
     * Chaine de connexion pour un enregistrement de base.
     *
     * @param array $rec conn_type, host, port, service_name, tns_alias, wallet_path
     * @return array{ok: bool, dsn: string, msg: string}
     */
    public static function build(array $rec): array
    {
        $type = ($rec['conn_type'] ?? 'standard') === 'cloud' ? 'cloud' : 'standard';

        if ($type === 'standard') {
            $host = trim((string)($rec['host'] ?? ''));
            $port = (int)($rec['port'] ?? 1521);
            $svc  = trim((string)($rec['service_name'] ?? ''));
            if ($host === '' || $svc === '') {
                return ['ok' => false, 'dsn' => '', 'msg' => t('demo_oracle.test_missing_params')];
            }
            if ($port < 1 || $port > 65535) $port = 1521;
            return ['ok' => true, 'dsn' => "$host:$port/$svc", 'msg' => ''];
        }

        $alias  = trim((string)($rec['tns_alias']   ?? ''));
        $wallet = trim((string)($rec['wallet_path'] ?? ''));
        if ($alias === '') {
            return ['ok' => false, 'dsn' => '', 'msg' => t('demo_oracle.test_missing_params')];
        }
        if ($wallet === '' || !is_dir($wallet)) {
            return ['ok' => false, 'dsn' => '', 'msg' => t('demo_oracle.cloud_no_wallet')];
        }

        /* TNS_ADMIN reste pose : c'est le filet si le descripteur n'a pas pu
           etre reconstitue et que l'alias part tel quel. */
        putenv('TNS_ADMIN=' . str_replace('\\', '/', $wallet));

        $dsn     = $alias;
        $tnsFile = $wallet . DIRECTORY_SEPARATOR . 'tnsnames.ora';
        if (is_file($tnsFile)) {
            $descriptor = self::tnsDescriptor((string)file_get_contents($tnsFile), $alias);
            if ($descriptor !== '') {
                $dsn = self::injectWalletDir($descriptor, $wallet);
            }
        }
        return ['ok' => true, 'dsn' => $dsn, 'msg' => ''];
    }

    private static function tnsDescriptor(string $content, string $alias): string
    {
        $lines = preg_split('/\r?\n/', $content);
        $flat  = '';
        foreach ($lines as $line) {
            if (preg_match('/^\s*#/', $line)) continue;
            if (preg_match('/^[^\s]/', $line)) $flat .= "\n";
            $flat .= rtrim($line);
        }
        $pattern = '/(?:^|\n)\s*' . preg_quote($alias, '/') . '\s*=\s*/i';
        if (!preg_match($pattern, $flat, $m, PREG_OFFSET_CAPTURE)) return '';
        $pos   = $m[0][1] + strlen($m[0][0]);
        $depth = 0; $start = -1; $len = strlen($flat);
        for ($i = $pos; $i < $len; $i++) {
            if ($flat[$i] === '(') {
                if ($depth === 0) $start = $i;
                $depth++;
            } elseif ($flat[$i] === ')') {
                $depth--;
                if ($depth === 0 && $start !== -1) return substr($flat, $start, $i - $start + 1);
            }
        }
        return '';
    }

    private static function injectWalletDir(string $descriptor, string $walletPath): string
    {
        $fwd       = str_replace('\\', '/', $walletPath);
        $upper     = strtoupper($descriptor);
        $searchPos = 0;
        while (($pos = strpos($upper, '(SECURITY=', $searchPos)) !== false) {
            $depth = 0; $len = strlen($descriptor);
            for ($i = $pos; $i < $len; $i++) {
                if ($descriptor[$i] === '(') $depth++;
                elseif ($descriptor[$i] === ')') {
                    $depth--;
                    if ($depth === 0) {
                        return substr($descriptor, 0, $i)
                             . '(MY_WALLET_DIRECTORY="' . $fwd . '")'
                             . substr($descriptor, $i);
                    }
                }
            }
            $searchPos = $pos + 1;
        }
        $last = strrpos($descriptor, ')');
        if ($last !== false) {
            return substr($descriptor, 0, $last)
                 . '(SECURITY=(MY_WALLET_DIRECTORY="' . $fwd . '"))'
                 . substr($descriptor, $last);
        }
        return $descriptor;
    }
}
