<?php
/**
 * Database backup helpers: mysqldump (with TCP host) + PDO SQL fallback.
 */
declare(strict_types=1);

/**
 * Human-readable size (avoids “0.00 MB” for small / error files).
 */
function nazareth_backup_format_bytes(int $bytes): string
{
    if ($bytes < 0) {
        $bytes = 0;
    }
    if ($bytes >= 1048576) {
        return number_format($bytes / 1048576, 2) . ' MB';
    }
    if ($bytes >= 1024) {
        return number_format($bytes / 1024, 2) . ' KB';
    }

    return (string) $bytes . ' bytes';
}

/**
 * True if dump file is missing, tiny, or contains mysqldump error text.
 */
function nazareth_sql_dump_file_looks_invalid(string $path): bool
{
    if (!is_file($path)) {
        return true;
    }
    $size = filesize($path);
    if ($size === false || $size < 64) {
        return true;
    }
    $head = file_get_contents($path, false, null, 0, min(8192, $size));
    if ($head === false || $head === '') {
        return true;
    }
    if (preg_match('/mysqldump:\\s*(Got error|Error|Couldn\'t execute|unknown option)/i', $head)) {
        return true;
    }
    if (stripos($head, 'command not found') !== false || stripos($head, 'not recognized') !== false) {
        return true;
    }

    return false;
}

/**
 * @return list<string>
 */
function nazareth_mysqldump_binary_candidates(): array
{
    $out = ['mysqldump'];
    if (PHP_OS_FAMILY === 'Darwin') {
        $amp = '/Applications/AMPPS/apps/mysql/bin/mysqldump';
        if (is_executable($amp)) {
            array_unshift($out, $amp);
        }
        $mamp = '/Applications/MAMP/Library/bin/mysqldump';
        if (is_executable($mamp)) {
            array_unshift($out, $mamp);
        }
    }

    return array_values(array_unique($out));
}

/**
 * Run mysqldump via shell; uses TCP host (same as config/database.php) so macOS socket mismatch is avoided.
 *
 * @return array{ok: bool, return_code: int, bytes: int, bin: string}
 */
function nazareth_run_mysqldump(string $bin, string $host, string $user, string $pass, string $dbname, string $outFile): array
{
    if (is_file($outFile)) {
        @unlink($outFile);
    }
    $port = '3306';
    $cmd = escapeshellcmd($bin)
        . ' -u ' . escapeshellarg($user)
        . ' -p' . escapeshellarg($pass)
        . ' -h ' . escapeshellarg($host)
        . ' -P ' . escapeshellarg($port)
        . ' --single-transaction --quick --skip-lock-tables '
        . escapeshellarg($dbname)
        . ' > ' . escapeshellarg($outFile)
        . ' 2>&1';

    $output = [];
    $returnCode = 1;
    exec($cmd, $output, $returnCode);
    $bytes = is_file($outFile) ? (int) filesize($outFile) : 0;

    return [
        'ok' => $returnCode === 0 && !nazareth_sql_dump_file_looks_invalid($outFile),
        'return_code' => $returnCode,
        'bytes' => $bytes,
        'bin' => $bin,
    ];
}

/**
 * Portable SQL export using PDO (same connection as the app). Slower than mysqldump; works without CLI.
 *
 * @throws RuntimeException
 */
function nazareth_export_database_pdo(PDO $pdo, string $path): void
{
    $f = fopen($path, 'wb');
    if ($f === false) {
        throw new RuntimeException('Cannot open backup file for writing.');
    }

    fwrite($f, "-- Nazareth SQL dump (PHP/PDO) " . gmdate('Y-m-d\TH:i:s\Z') . "\n");
    fwrite($f, "SET NAMES utf8mb4;\nSET FOREIGN_KEY_CHECKS=0;\n\n");

    $stmt = $pdo->query("SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'");
    if (!$stmt) {
        fclose($f);
        throw new RuntimeException('Could not list tables.');
    }
    $tables = $stmt->fetchAll(PDO::FETCH_NUM);
    foreach ($tables as $row) {
        $table = (string) $row[0];
        if (!preg_match('/^[a-zA-Z0-9_]+$/', $table)) {
            continue;
        }
        $tq = '`' . str_replace('`', '``', $table) . '`';
        $createStmt = $pdo->query('SHOW CREATE TABLE ' . $tq);
        if (!$createStmt) {
            fclose($f);
            throw new RuntimeException('SHOW CREATE failed for ' . $table);
        }
        $meta = $createStmt->fetch(PDO::FETCH_ASSOC);
        $ddl = $meta['Create Table'] ?? '';
        fwrite($f, "\nDROP TABLE IF EXISTS {$tq};\n{$ddl};\n\n");

        $data = $pdo->query('SELECT * FROM ' . $tq);
        if (!$data) {
            continue;
        }
        $columns = null;
        $colList = '';
        while ($r = $data->fetch(PDO::FETCH_ASSOC)) {
            if ($columns === null) {
                $columns = array_keys($r);
                $safeCols = array_map(static function ($c) {
                    return '`' . str_replace('`', '``', $c) . '`';
                }, $columns);
                $colList = implode(',', $safeCols);
            }
            $vals = [];
            foreach ($columns as $col) {
                $v = $r[$col];
                if ($v === null) {
                    $vals[] = 'NULL';
                } elseif (is_int($v) || is_float($v)) {
                    $vals[] = (string) $v;
                } else {
                    $vals[] = $pdo->quote((string) $v);
                }
            }
            fwrite($f, 'INSERT INTO ' . $tq . ' (' . $colList . ') VALUES (' . implode(',', $vals) . ");\n");
        }
    }

    fwrite($f, "\nSET FOREIGN_KEY_CHECKS=1;\n");
    fclose($f);
}
