<?php
// web-files/api/valkey.php
// Customer-facing Valkey management API.
// Spec: docs/superpowers/specs/2026-04-20-customer-valkey-design.md
//
// Root cannot manage its own Valkey via this endpoint — the feature is
// per-customer. Root must supply target_user to act on a customer's
// instance. For server-side caching needs, use docker-management instead.

header('Content-Type: application/json');
require_once __DIR__ . '/../libs/permission_checker.php';
require_once __DIR__ . '/../libs/valkey-mgmt.php';
require_once __DIR__ . '/../libs/mysqlmgmt.php';

check_api_permission('valkey');
if (!defined('AUTH_USER')) {
    http_response_code(401);
    echo json_encode(['success' => false, 'error' => 'unauthenticated']);
    exit;
}

$is_root = (AUTH_USER === 'root');
$input = json_decode(file_get_contents('php://input'), true) ?: [];
$action = $input['action'] ?? ($_GET['action'] ?? '');

$m = new mysqlmgmt();
$db = $m->getMySQLConnection();

// ---- target-user resolution --------------------------------------------
//
// Non-root: always acts on their own account. Any submitted target_user is
// ignored (defence against a caller trying to escalate).
//
// Root: must provide target_user; cannot manage a Valkey instance for itself.
// list_customers is exempt (it's the action the root UI calls to populate the
// customer dropdown before any target has been chosen).
// Actions that don't act on a specific customer and so don't need target_user.
$no_target_actions = ['list_customers', 'admin_list', 'admin_pull_image', 'whoami'];

if ($is_root) {
    $submitted = $input['target_user'] ?? ($_GET['target_user'] ?? '');
    if (in_array($action, $no_target_actions, true)) {
        $username = null; // not used
    } else {
        if (empty($submitted) || $submitted === 'root') {
            http_response_code(400);
            echo json_encode(['success' => false, 'error' => 'root must select a customer (target_user required)']);
            exit;
        }
        // Validate submitted username exists as a WHP customer account.
        $chk = $db->prepare("SELECT 1 FROM whp.client_allowances WHERE username = ?");
        $chk->execute([$submitted]);
        if (!$chk->fetchColumn()) {
            http_response_code(400);
            echo json_encode(['success' => false, 'error' => 'unknown target_user']);
            exit;
        }
        $username = $submitted;
    }
} else {
    $username = AUTH_USER;
}

$vk = new ValkeyMgr($db);

// App-containers list scopes by the resolved $username (not AUTH_USER).
$app_containers = [];
if ($username !== null) {
    $appStmt = $db->prepare("
        SELECT sc.container_id
        FROM whp.site_containers sc
        JOIN whp.sites s ON s.id = sc.site_id
        WHERE s.username = ?
    ");
    $appStmt->execute([$username]);
    $app_containers = $appStmt->fetchAll(PDO::FETCH_COLUMN) ?: [];
}

try {
    switch ($action) {
        case 'whoami':
            // Lightweight discovery for the UI: am I root, and if so which customers can I manage?
            echo json_encode([
                'success' => true,
                'is_root' => $is_root,
                'auth_user' => AUTH_USER
            ]);
            break;

        case 'list_customers': {
            if (!$is_root) {
                http_response_code(403);
                echo json_encode(['success' => false, 'error' => 'root only']);
                break;
            }
            $stmt = $db->query("SELECT username FROM whp.client_allowances WHERE username <> 'root' ORDER BY username ASC");
            echo json_encode([
                'success' => true,
                'customers' => $stmt->fetchAll(PDO::FETCH_COLUMN) ?: []
            ]);
            break;
        }

        case 'status':
            echo json_encode(['success' => true, 'info' => $vk->get_info($username)]);
            break;

        case 'enable': {
            $cfg = [
                'memory_mb'         => intval($input['memory_mb'] ?? 0),
                'mode'              => strval($input['mode'] ?? 'cache'),
                'eviction_policy'   => strval($input['eviction_policy'] ?? 'allkeys-lru'),
            ];

            // Quota check against the target customer's allowance (not root's).
            // Uncapped users skip the RAM check entirely — they're billed for the whole box.
            $usage = $m->getUserResourceUsage($username);
            $effective = $m->getEffectiveLimits($username);
            if (!$effective['uncapped']) {
                $max_memory_mb = intval($effective['memory_limit']);
                $remaining = $max_memory_mb - intval($usage['memory_used'] ?? 0);
                if ($cfg['memory_mb'] > $remaining) {
                    echo json_encode([
                        'success' => false,
                        'error' => "insufficient RAM: requested {$cfg['memory_mb']} MB, remaining {$remaining} MB"
                    ]);
                    break;
                }
            }

            echo json_encode($vk->enable($username, $cfg, $app_containers));
            break;
        }

        case 'disable': {
            $backup_hook = function($u) use ($db) {
                try {
                    $tStmt = $db->prepare("SELECT id FROM whp.backup_targets WHERE user = ? ORDER BY id ASC LIMIT 1");
                    $tStmt->execute([$u]);
                    $target_id = $tStmt->fetchColumn();
                    if (!$target_id) {
                        error_log("valkey disable: no backup_target for user $u — skipping final snapshot backup");
                        return;
                    }
                    require_once __DIR__ . '/../libs/BackupEngine.php';
                    $be = new BackupEngine();
                    $be->createUserfilesBackup($u, intval($target_id));
                } catch (Throwable $e) {
                    error_log("valkey disable backup_hook: " . $e->getMessage());
                }
            };
            echo json_encode($vk->disable($username, $app_containers, $backup_hook));
            break;
        }

        case 'resize':
            echo json_encode($vk->resize($username, intval($input['memory_mb'] ?? 0), $app_containers));
            break;

        case 'recreate':
            // Customer-facing: recreate my own instance (or root-as-customer via target_user).
            // Acts on the resolved $username, so non-root can't affect anyone else.
            echo json_encode($vk->recreate($username, $app_containers));
            break;

        case 'admin_list': {
            if (!$is_root) {
                http_response_code(403);
                echo json_encode(['success' => false, 'error' => 'root only']);
                break;
            }
            echo json_encode(['success' => true, 'instances' => $vk->list_fleet()]);
            break;
        }

        case 'admin_pull_image': {
            if (!$is_root) {
                http_response_code(403);
                echo json_encode(['success' => false, 'error' => 'root only']);
                break;
            }
            $res = $vk->pull_image();
            echo json_encode(['success' => !empty($res['success']), 'result' => $res]);
            break;
        }

        case 'admin_restart': {
            if (!$is_root) {
                http_response_code(403);
                echo json_encode(['success' => false, 'error' => 'root only']);
                break;
            }
            // $username is already the validated target from the root guard above.
            echo json_encode($vk->restart($username));
            break;
        }

        case 'admin_recreate': {
            if (!$is_root) {
                http_response_code(403);
                echo json_encode(['success' => false, 'error' => 'root only']);
                break;
            }
            echo json_encode($vk->recreate($username, $app_containers));
            break;
        }

        default:
            http_response_code(400);
            echo json_encode(['success' => false, 'error' => 'unknown action']);
    }
} catch (Throwable $e) {
    http_response_code(500);
    error_log('api/valkey.php: ' . $e->getMessage());
    echo json_encode(['success' => false, 'error' => 'internal error']);
}
