<?php
/* ============================================================================
   WBMCZ — ACL MIGRATION  (detailed Role & Access control)
   Run this ONCE on the live server:   https://wbmcz.online/aclmigration.php

   What it does
     1. roles     → level, perms_json, description, status, created_by, updated_at
     2. users     → staff_level, perm_extra, perm_denied
     3. staff_form_fields   the fields on the Add Admin / Add Employee form
     4. staff_field_values  what was filled in for each staff account
     5. staff_access_log    who gave what access to whom, and who took it back
     6. Every existing role is expanded: the modules it already held are opened
        completely — every action and every column inside them — so nobody
        loses a single thing on the day this goes live.
     7. Every staff account is stamped with a level:
          no role, or a role holding 'all'  → super admin
          a role that held site settings or staff & roles → admin
          everyone else → employee
        (You can change any of these afterwards on the Staff screen.)

   Safe to run more than once — every step checks before it changes anything.
   DELETE THIS FILE after a successful run.
   ============================================================================ */

require_once __DIR__ . '/config.php';
require_once __DIR__ . '/db.php';

$log = [];
$conn = $db->getConnection();

if (!@is_file(__DIR__ . '/includes/acl.php')) {
    $log[] = ['fail', 'includes/acl.php', 'Upload includes/acl.php first, then open this page again.'];
} else {
    require_once __DIR__ . '/includes/acl.php';
}

function acm_col_exists($conn, $t, $c) {
    try { $r = $conn->query("SHOW COLUMNS FROM `$t` LIKE '$c'"); return $r && $r->num_rows > 0; }
    catch (\Throwable $e) { return false; }
}
function acm_table_exists($conn, $t) {
    try { $r = $conn->query("SHOW TABLES LIKE '$t'"); return $r && $r->num_rows > 0; }
    catch (\Throwable $e) { return false; }
}

if (function_exists('acl_ensure_schema')) {

    /* ---- 1 & 2 & 3 & 4 & 5 : everything the engine needs ---- */
    $before = [];
    foreach (['roles.level','roles.perms_json','roles.description','roles.status','roles.created_by',
              'roles.created_by_name','roles.updated_at','users.staff_level','users.perm_extra','users.perm_denied'] as $p) {
        list($t, $c) = explode('.', $p);
        $before[$p] = acm_col_exists($conn, $t, $c);
    }
    $tbefore = [];
    foreach (['staff_form_fields','staff_field_values','staff_access_log'] as $t) { $tbefore[$t] = acm_table_exists($conn, $t); }

    acl_ensure_schema($db);

    foreach ($before as $p => $had) {
        $log[] = $had ? ['ok', $p, 'already present'] : ['add', $p, 'column added'];
    }
    foreach ($tbefore as $t => $had) {
        $log[] = $had ? ['ok', $t, 'table already exists'] : ['add', $t, 'table created'];
    }

    /* ---- the fields on the staff creation form ---- */
    try { $n = acl_seed_fields($db); $log[] = ['add', 'staff_form_fields', $n . ' standard field(s) written in']; }
    catch (\Throwable $e) { $log[] = ['fail', 'staff_form_fields seed', $e->getMessage()]; }

    /* ---- 6 : expand every existing role, without taking anything away ---- */
    try {
        $rows = $db->select("SELECT id, name, permissions, perms_json, level FROM roles ORDER BY id ASC");
        foreach ($rows as $r) {
            $rid  = (int)$r['id'];
            $old  = json_decode((string)$r['permissions'], true);
            $old  = is_array($old) ? $old : [];
            $have = json_decode((string)($r['perms_json'] ?? ''), true);

            /* the full-access role keeps its star and becomes the super admin role */
            if (in_array('all', $old, true)) {
                if (empty($have)) {
                    $db->insert("UPDATE roles SET perms_json = ?, level = 'superadmin' WHERE id = ?", [json_encode(['*']), $rid]);
                    $log[] = ['add', 'role: ' . $r['name'], 'full system access — kept as super admin'];
                } else {
                    $log[] = ['ok', 'role: ' . $r['name'], 'already detailed'];
                }
                continue;
            }

            if (!empty($have) && is_array($have)) {
                $log[] = ['ok', 'role: ' . $r['name'], 'already detailed — left untouched'];
                continue;
            }

            $full = acl_expand_legacy($old);
            /* a role that could change site settings or manage staff is an admin role */
            $lvl  = (in_array('manage_settings', $old, true) || in_array('manage_staff_roles', $old, true))
                    ? 'admin' : 'employee';
            $db->insert("UPDATE roles SET perms_json = ?, level = ? WHERE id = ?", [json_encode($full), $lvl, $rid]);
            $log[] = ['add', 'role: ' . $r['name'],
                      count($full) . ' permission(s) opened across ' . count($old) . ' module group(s) — level: ' . $lvl];
        }
    } catch (\Throwable $e) { $log[] = ['fail', 'roles expand', $e->getMessage()]; }

    /* ---- 7 : stamp a level on every staff account ---- */
    try {
        $staff = $db->select("SELECT u.id, u.name, u.role_id, u.staff_level, r.level AS role_level, r.permissions
                              FROM users u LEFT JOIN roles r ON r.id = u.role_id
                              WHERE u.user_type = 'admin'");
        $counts = ['superadmin' => 0, 'admin' => 0, 'employee' => 0, 'kept' => 0];
        foreach ($staff as $s) {
            if (!empty($s['staff_level'])) { $counts['kept']++; continue; }
            $p = json_decode((string)($s['permissions'] ?? ''), true);
            if (empty($s['role_id']) || (is_array($p) && in_array('all', $p, true))) { $lvl = 'superadmin'; }
            elseif (($s['role_level'] ?? '') === 'admin' || ($s['role_level'] ?? '') === 'superadmin') { $lvl = ($s['role_level'] === 'superadmin') ? 'superadmin' : 'admin'; }
            else { $lvl = 'employee'; }
            $db->insert("UPDATE users SET staff_level = ? WHERE id = ?", [$lvl, (int)$s['id']]);
            $counts[$lvl]++;
        }
        $log[] = ['add', 'users.staff_level',
            "{$counts['superadmin']} super admin, {$counts['admin']} admin, {$counts['employee']} employee"
            . ($counts['kept'] ? ", {$counts['kept']} already stamped" : '')];
    } catch (\Throwable $e) { $log[] = ['fail', 'staff levels', $e->getMessage()]; }

    /* ---- the new Staff & Roles modules join every full-access role ---- */
    try {
        $rs = $conn->query("SELECT id, permissions FROM roles");
        $t = 0;
        while ($rs && ($r = $rs->fetch_assoc())) {
            $p = json_decode((string)$r['permissions'], true);
            if (!is_array($p) || !in_array('all', $p, true)) continue;
            foreach (['manage_tasks', 'manage_subscriptions'] as $need) {
                if (!in_array($need, $p, true)) { $p[] = $need; $t++; }
            }
            $st = $conn->prepare("UPDATE roles SET permissions = ? WHERE id = ?");
            $j = json_encode(array_values($p)); $st->bind_param('si', $j, $r['id']); $st->execute(); $st->close();
        }
        $log[] = ['ok', 'roles legacy list', $t > 0 ? "$t entry added" : 'nothing needed changing'];
    } catch (\Throwable $e) { $log[] = ['fail', 'roles legacy list', $e->getMessage()]; }
}

$fails = 0; $adds = 0;
foreach ($log as $l) { if ($l[0] === 'fail') $fails++; if ($l[0] === 'add') $adds++; }
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ACL Migration — Role &amp; Access</title>
    <script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-slate-100 p-6 font-sans">
    <div class="max-w-3xl mx-auto bg-white rounded-2xl shadow p-6 border border-slate-200">
        <h1 class="text-2xl font-extrabold text-slate-800 mb-1">ACL Migration — detailed Role &amp; Access</h1>
        <p class="text-sm text-slate-500 mb-5">
            Database: <b><?php echo DB_NAME; ?></b> &middot;
            <?php if (function_exists('acl_all_keys')): ?>
                <b><?php echo count(acl_all_keys()); ?></b> permission points across
                <b><?php echo count(acl_modules()); ?></b> modules
            <?php endif; ?>
        </p>

        <div class="mb-5 rounded-xl px-4 py-3 <?php echo $fails ? 'bg-rose-50 text-rose-700 border border-rose-200' : 'bg-emerald-50 text-emerald-700 border border-emerald-200'; ?>">
            <?php echo $fails
                ? "Finished with $fails error(s) — see the rows marked FAIL."
                : "Migration completed successfully — $adds change(s) applied. Nobody lost any access."; ?>
        </div>

        <table class="w-full text-sm border-collapse">
            <thead>
                <tr class="bg-slate-50 text-slate-500 text-xs uppercase tracking-wider">
                    <th class="text-left py-2 px-3">Result</th>
                    <th class="text-left py-2 px-3">Target</th>
                    <th class="text-left py-2 px-3">Detail</th>
                </tr>
            </thead>
            <tbody class="divide-y divide-slate-100">
            <?php foreach ($log as $l):
                $cls = ['ok' => 'text-slate-500', 'add' => 'text-emerald-600', 'skip' => 'text-amber-600', 'fail' => 'text-rose-600'][$l[0]] ?? '';
            ?>
                <tr>
                    <td class="py-2 px-3 font-bold uppercase <?php echo $cls; ?>"><?php echo $l[0]; ?></td>
                    <td class="py-2 px-3 font-mono text-slate-700 text-xs"><?php echo htmlspecialchars($l[1]); ?></td>
                    <td class="py-2 px-3 text-slate-500 text-xs"><?php echo htmlspecialchars($l[2]); ?></td>
                </tr>
            <?php endforeach; ?>
            </tbody>
        </table>

        <div class="mt-6 text-xs text-slate-600 space-y-2 bg-slate-50 border border-slate-200 rounded-xl p-4">
            <p class="font-bold text-slate-700 text-sm">Where to go next</p>
            <p><b>Admin &rarr; User Management &rarr; Custom Roles</b> — every role now opens into modules, and inside each module you tick the actions and the columns separately.</p>
            <p><b>Admin &rarr; User Management &rarr; Staff Accounts</b> — create an Admin or an Employee, and give that one person a little more or a little less than their role.</p>
            <p><b>Admin &rarr; User Management &rarr; Staff Form Fields</b> — decide which boxes appear on the Add Admin and Add Employee forms.</p>
            <p class="text-slate-500">An admin can only ever hand out what they hold themselves, and can only create employees. That rule is enforced on the server, not just hidden in the page.</p>
        </div>

        <p class="mt-4 text-xs text-rose-600 font-bold">Delete aclmigration.php from the server once this page shows &ldquo;completed successfully&rdquo;.</p>
    </div>
</body>
</html>
