<?php
/**
 * wipe-inventory.php — șterge TOT inventory-ul (vehicule + poze) de pe admin.
 *
 * Pas 1 (doar citește, nu șterge nimic):
 *   https://admingrandfather.com/wipe-inventory.php?key=CHEIA
 * Pas 2 (execută, după ce ai verificat planul):
 *   https://admingrandfather.com/wipe-inventory.php?key=CHEIA&apply=1&confirm=STERGE
 *
 * Înainte de ștergere face backup: rândurile din tabele (JSON) și pozele sunt MUTATE în
 *   /home/USER/.trash/inventory_wipe_DATA/
 * Când ești sigur că totul e ok, golești folderul acela din File Manager.
 * Șterge acest fișier de pe server după folosire.
 */

const KEY = '7037e3fe831aab0690c9972ac7076547';

// Tabele de inventory: tot ce se potrivește cu regex-ul de mai jos, minus excluderile.
// Poți forța lista exactă cu &tables=vehicles,vehicle_photos,vehicle_sites
const TABLE_MATCH   = '/vehicle|inventor|car_|cars$|photo|image|gallery|media/i';
const TABLE_EXCLUDE = '/^(sites|review_sites|users?|admins?|settings|site_settings)$/i';
const IMG_EXT       = '/\.(jpe?g|png|webp|gif|avif|heic|bmp)$/i';

if (!isset($_GET['key']) || !hash_equals(KEY, (string)$_GET['key'])) { http_response_code(404); exit('Not found'); }
@set_time_limit(600);
error_reporting(E_ALL); ini_set('display_errors', '1');

function h($s) { return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); }
function q($n) { return '`' . str_replace('`', '``', $n) . '`'; }

$DOC  = rtrim($_SERVER['DOCUMENT_ROOT'] ?: __DIR__, '/');
$HOME = dirname($DOC);
$APPLY = ($_GET['apply'] ?? '') === '1' && ($_GET['confirm'] ?? '') === 'STERGE';
$FORCE_FK = ($_GET['force_fk'] ?? '') === '1';
$log = []; $warn = []; $err = [];

// ---------- DB ----------
if (is_file($DOC . '/config.php')) require_once $DOC . '/config.php';
if (!defined('DB_NAME')) exit('Nu găsesc config.php cu DB_* în ' . h($DOC));
try {
    $pdo = new PDO('mysql:host=' . DB_HOST . ';dbname=' . DB_NAME . ';charset=utf8mb4', DB_USER, DB_PASS,
        [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC]);
} catch (Throwable $e) { exit('DB: ' . h($e->getMessage())); }

$allTables = $pdo->query('SHOW TABLES')->fetchAll(PDO::FETCH_COLUMN);
if (!empty($_GET['tables'])) {
    $targets = array_values(array_intersect($allTables, array_map('trim', explode(',', $_GET['tables']))));
} else {
    $targets = array_values(array_filter($allTables, function ($t) { return preg_match(TABLE_MATCH, $t) && !preg_match(TABLE_EXCLUDE, $t); }));
}

// ---------- info tabele ----------
$info = [];
foreach ($targets as $t) {
    $cols = array_column($pdo->query('SHOW COLUMNS FROM ' . q($t))->fetchAll(), 'Field');
    $info[$t] = ['cols' => $cols, 'count' => (int)$pdo->query('SELECT COUNT(*) FROM ' . q($t))->fetchColumn()];
}

// ---------- chei străine din afara listei ----------
$fks = $pdo->prepare("SELECT k.TABLE_NAME, k.COLUMN_NAME, k.REFERENCED_TABLE_NAME, rc.DELETE_RULE
    FROM information_schema.KEY_COLUMN_USAGE k
    JOIN information_schema.REFERENTIAL_CONSTRAINTS rc
      ON rc.CONSTRAINT_NAME = k.CONSTRAINT_NAME AND rc.CONSTRAINT_SCHEMA = k.TABLE_SCHEMA AND rc.TABLE_NAME = k.TABLE_NAME
    WHERE k.TABLE_SCHEMA = ? AND k.REFERENCED_TABLE_NAME IS NOT NULL");
$fks->execute([DB_NAME]);
$externalFk = []; $referenced = [];
foreach ($fks->fetchAll() as $fk) {
    $inT = in_array($fk['REFERENCED_TABLE_NAME'], $targets, true);
    if ($inT && in_array($fk['TABLE_NAME'], $targets, true)) $referenced[$fk['REFERENCED_TABLE_NAME']] = true;
    if ($inT && !in_array($fk['TABLE_NAME'], $targets, true)) {
        if ($fk['DELETE_RULE'] === 'SET NULL') { $warn[] = h($fk['TABLE_NAME']) . '.' . h($fk['COLUMN_NAME']) . ' → SET NULL: mesajele rămân, legătura spre vehicul devine NULL. OK.'; continue; }
        $externalFk[] = $fk;
        $warn[] = "Tabela <b>" . h($fk['TABLE_NAME']) . "</b>." . h($fk['COLUMN_NAME']) . " referă <b>" . h($fk['REFERENCED_TABLE_NAME']) . "</b> (ON DELETE " . h($fk['DELETE_RULE']) . ")"
            . ($fk['DELETE_RULE'] === 'CASCADE' ? ' — <b>ar șterge și rândurile din ' . h($fk['TABLE_NAME']) . '!</b>' : ' — ștergerea poate fi blocată.');
    }
}
// copiii înaintea părinților (vehicles la final)
usort($targets, function ($a, $b) use ($referenced) { return (isset($referenced[$a]) <=> isset($referenced[$b])); });

// ---------- poze referite în DB ----------
$protected = []; // fișiere folosite de tabele care NU se șterg (logo-uri etc.)
foreach (array_diff($allTables, $targets) as $t) {
    foreach ($pdo->query('SELECT * FROM ' . q($t) . ' LIMIT 5000') as $r)
        foreach ($r as $v) if (is_string($v) && preg_match('/\.(jpe?g|png|webp|gif|avif|svg)/i', $v)) $protected[] = $v;
}
function resolveFile($v, $DOC) {
    $v = trim($v);
    if ($v === '' || !preg_match(IMG_EXT, parse_url($v, PHP_URL_PATH) ?: $v)) return null;
    $p = preg_match('#^https?://#i', $v) ? (parse_url($v, PHP_URL_PATH) ?: '') : $v;
    foreach ([$DOC . '/' . ltrim($p, '/'), $p] as $c) { $rp = realpath($c); if ($rp && is_file($rp) && strpos($rp, $DOC) === 0) return $rp; }
    return null;
}
function extractStrings($v) {
    if (!is_string($v) || $v === '') return [];
    $j = json_decode($v, true);
    if (is_array($j)) { $o = []; array_walk_recursive($j, function ($x) use (&$o) { if (is_string($x)) $o[] = $x; }); return $o; }
    return preg_split('/[,\s|]+/', $v);
}
$files = []; $missing = 0;
foreach ($targets as $t) {
    $imgCols = array_filter($info[$t]['cols'], function ($c) { return preg_match('/photo|image|img|path|file|thumb|url|gallery|cover|pic/i', $c); });
    if (!$imgCols) continue;
    foreach ($pdo->query('SELECT ' . implode(',', array_map('q', $imgCols)) . ' FROM ' . q($t)) as $r)
        foreach ($r as $v) foreach (extractStrings($v) as $s) {
            if (!preg_match(IMG_EXT, parse_url($s, PHP_URL_PATH) ?: $s)) continue;
            $f = resolveFile($s, $DOC);
            if ($f) $files[$f] = 'DB:' . $t; else $missing++;
        }
}

// ---------- foldere de upload dedicate vehiculelor ----------
$dirs = [];
$it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($DOC, FilesystemIterator::SKIP_DOTS), RecursiveIteratorIterator::SELF_FIRST, RecursiveIteratorIterator::CATCH_GET_CHILD);
$it->setMaxDepth(3);
foreach ($it as $d) {
    if (!$d->isDir()) continue;
    $rel = substr($d->getPathname(), strlen($DOC) + 1);
    if (preg_match('#(^|/)(\.|admin/_satellite|core|api|vendor|node_modules)#', $rel)) continue;
    if (preg_match('#(upload|media|img|images|photos|storage)#i', $rel) && preg_match('#(vehicle|inventor|cars?$|cars/|photos?$|gallery)#i', basename($rel)) && !preg_match('/logo|brand|site/i', $rel))
        $dirs[] = $d->getPathname();
}
$dirs = array_values(array_filter($dirs, function ($d) use ($dirs) { foreach ($dirs as $o) if ($o !== $d && strpos($d, $o . '/') === 0) return false; return true; }));
foreach ($dirs as $d) {
    $ri = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($d, FilesystemIterator::SKIP_DOTS), RecursiveIteratorIterator::LEAVES_ONLY, RecursiveIteratorIterator::CATCH_GET_CHILD);
    foreach ($ri as $f) if ($f->isFile() && preg_match(IMG_EXT, $f->getFilename())) $files[$f->getRealPath()] = $files[$f->getRealPath()] ?? 'folder:' . substr($d, strlen($DOC));
}
// scoate fișierele folosite în altă parte (logo-uri etc.)
$skipped = 0;
foreach (array_keys($files) as $f) {
    $rel = substr($f, strlen($DOC));
    foreach ($protected as $p) if (strpos($p, basename($f)) !== false && strpos($p, ltrim(dirname($rel), '/')) !== false) { unset($files[$f]); $skipped++; break; }
}
$bytes = 0; foreach (array_keys($files) as $f) $bytes += @filesize($f);

// ---------- EXECUȚIE ----------
$done = false;
if ($APPLY) {
    if ($externalFk && !$FORCE_FK) {
        $err[] = 'Oprit: alte tabele depind de inventory (vezi avertismentele). Dacă ești sigur, adaugă &force_fk=1.';
    } elseif (!$targets) {
        $err[] = 'Nicio tabelă de șters.';
    } else {
        $bk = $HOME . '/.trash/inventory_wipe_' . date('Ymd_His');
        if (!@mkdir($bk . '/files', 0755, true)) { $err[] = 'Nu pot crea ' . h($bk); }
        else {
            // 1. backup tabele
            $dump = [];
            foreach ($targets as $t) $dump[$t] = $pdo->query('SELECT * FROM ' . q($t))->fetchAll();
            file_put_contents($bk . '/tables.json', json_encode($dump, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
            $log[] = 'Backup rânduri → ' . h($bk . '/tables.json');
            // 2. ștergere rânduri
            try {
                if ($FORCE_FK) $pdo->exec('SET FOREIGN_KEY_CHECKS=0');
                $pdo->beginTransaction();
                foreach ($targets as $t) { $n = $pdo->exec('DELETE FROM ' . q($t)); $log[] = 'DELETE ' . h($t) . ': ' . (int)$n . ' rânduri'; }
                $pdo->commit();
                $pdo->exec('SET FOREIGN_KEY_CHECKS=1');
                foreach ($targets as $t) $pdo->exec('ALTER TABLE ' . q($t) . ' AUTO_INCREMENT = 1');
                $done = true;
            } catch (Throwable $e) {
                if ($pdo->inTransaction()) $pdo->rollBack();
                $pdo->exec('SET FOREIGN_KEY_CHECKS=1');
                $err[] = 'DB rollback: ' . h($e->getMessage()) . ' — nu s-a șters nimic, pozele nu au fost mutate.';
            }
            // 3. mutare poze
            if ($done) {
                $moved = 0; $fail = 0;
                foreach (array_keys($files) as $f) {
                    $dst = $bk . '/files' . substr($f, strlen($DOC));
                    @mkdir(dirname($dst), 0755, true);
                    if (@rename($f, $dst)) $moved++; else $fail++;
                }
                $log[] = "Poze mutate în backup: $moved" . ($fail ? ", eșuate: $fail" : '');
                $log[] = 'Când ești sigur, golește din File Manager: <code>' . h($bk) . '</code>';
            }
        }
    }
}
$self = h('?key=' . urlencode($_GET['key']) . (!empty($_GET['tables']) ? '&tables=' . urlencode($_GET['tables']) : ''));
?><!doctype html><html lang="ro"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>Wipe inventory</title>
<style>body{font:14px/1.5 system-ui,sans-serif;margin:0;padding:16px;background:#f6f7f9;color:#1d2330}
section{background:#fff;border:1px solid #e2e5ea;border-radius:8px;padding:14px 18px;margin:14px 0}h2{margin:0 0 8px;font-size:17px}
table{border-collapse:collapse;width:100%}td,th{border-bottom:1px solid #eef0f3;padding:4px 8px;text-align:left;vertical-align:top;word-break:break-all}
.bad{color:#b3261e}.warn{color:#a15c00}.ok{color:#127a3a}.btn{display:inline-block;background:#b3261e;color:#fff;padding:10px 16px;border-radius:6px;text-decoration:none;font-weight:600}
code{font-size:12.5px}</style></head><body>
<h1><?= $done ? '<span class="ok">Inventory șters</span>' : ($APPLY ? 'Execuție' : 'Plan de ștergere (nimic nu s-a șters încă)') ?></h1>
<?php foreach ($err as $m) echo '<section class="bad"><b>' . $m . '</b></section>'; ?>
<?php if ($log) { echo '<section><h2>Rezultat</h2><ul>'; foreach ($log as $m) echo "<li>$m</li>"; echo '</ul></section>'; } ?>
<?php if ($warn) { echo '<section class="warn"><h2>Atenție</h2><ul>'; foreach ($warn as $m) echo "<li>$m</li>"; echo '</ul></section>'; } ?>
<section><h2>Tabele care se golesc (<?= count($targets) ?>)</h2><table><tr><th>Tabelă</th><th>Rânduri</th><th>Coloane</th></tr>
<?php foreach ($info as $t => $i) echo '<tr><td><b>' . h($t) . '</b></td><td>' . $i['count'] . '</td><td><code>' . h(implode(', ', $i['cols'])) . '</code></td></tr>'; ?>
</table><p>Rămân neatinse: <code><?= h(implode(', ', array_diff($allTables, $targets))) ?></code></p>
<p>Lista e greșită? Rulează cu <code>&tables=tabel1,tabel2</code>.</p></section>
<section><h2>Poze care se mută în backup (<?= count($files) ?> fișiere, <?= round($bytes / 1048576, 1) ?> MB)</h2>
<p>Foldere de vehicule detectate: <code><?= h(implode(', ', array_map(function ($d) use ($DOC) { return substr($d, strlen($DOC)); }, $dirs)) ?: '—') ?></code></p>
<p>Referințe din DB fără fișier pe disc: <?= $missing ?> · Fișiere protejate (folosite de alte tabele, ex. logo): <?= $skipped ?></p>
<table><?php $i = 0; foreach ($files as $f => $src) { if (++$i > 40) { echo '<tr><td colspan="2">… și încă ' . (count($files) - 40) . '</td></tr>'; break; } echo '<tr><td><code>' . h(substr($f, strlen($DOC))) . '</code></td><td>' . h($src) . '</td></tr>'; } ?></table></section>
<?php if (!$APPLY): ?>
<section><p>Verifică listele de mai sus. Backup-ul (rânduri + poze) merge în <code><?= h($HOME) ?>/.trash/inventory_wipe_DATA/</code>.</p>
<a class="btn" href="<?= $self ?>&apply=1&confirm=STERGE" onclick="return confirm('Ștergi tot inventory-ul? Rândurile și pozele merg în backup în .trash.')">Șterge tot inventory-ul</a></section>
<?php endif; ?>
<p class="bad"><b>Șterge wipe-inventory.php de pe server după folosire.</b></p>
</body></html>
