<?php
/**
 * /api/dashboard/bulk.php — bulk operations on documents.
 *
 * POST body: { op: "move"|"retag"|"recategorize"|"trash"|"restore",
 *              ids: [1,2,3,...],
 *              ...op-specific args }
 *
 * Op args:
 *   move:         { folder_id }              — null/0 = unassigned
 *   retag:        { tags: "a,b,c", mode: "replace"|"append"|"remove" }
 *   recategorize: { category: "slug" }
 *   trash:        {}                          — soft delete
 *   restore:      {}                          — un-trash
 *
 * Max 500 IDs per call.
 */

declare(strict_types=1);

require_once dirname(__DIR__, 2) . '/includes/bootstrap.php';

dbnToolsRequireMethod('POST');
dbnToolsRequireAuth();

try {
    $tenant = dbnToolsEnsureDashboardTenant();
} catch (DbnToolsHttpException $e) {
    dbnToolsError($e->getMessage(), $e->status, $e->errorCode);
}
$clientId   = (int)$tenant['client_id'];
$userId     = (int)($tenant['client_user_id'] ?? 0);
$tenantRole = (string)($tenant['role'] ?? 'editor');

$db    = dbnToolsDb();
$input = dbnToolsJsonInput(200_000);
$op    = (string)($input['op'] ?? '');
$ids   = $input['ids'] ?? [];

if (!is_array($ids) || !$ids) {
    dbnToolsError('ids array is required.', 400, 'missing_ids');
}
$ids = array_values(array_unique(array_filter(array_map('intval', $ids), fn($v) => $v > 0)));
if (!$ids) {
    dbnToolsError('No valid ids.', 400, 'invalid_ids');
}
if (count($ids) > 500) {
    dbnToolsError('Maximum 500 ids per bulk operation.', 422, 'too_many');
}

$ph = implode(',', array_fill(0, count($ids), '?'));

// Load + ACL-check
$rows = $db->prepare("SELECT id, folder_id FROM client_documents WHERE client_id = ? AND id IN ({$ph}) AND deleted_at IS NULL");
$rows->execute(array_merge([$clientId], $ids));
$allowedIds = [];
$perDoc = [];
foreach ($rows->fetchAll() as $r) {
    $fid = $r['folder_id'] ? (int)$r['folder_id'] : 0;
    if (dbnDmsUserCanAccessFolder($fid ?: null, 'write', $clientId, $userId, $tenantRole)) {
        $allowedIds[] = (int)$r['id'];
        $perDoc[(int)$r['id']] = $fid;
    }
}
if (!$allowedIds) {
    dbnToolsError('No accessible documents in selection.', 403, 'forbidden');
}

try {
    switch ($op) {
        case 'move':         $result = opMove($db, $clientId, $userId, $tenantRole, $allowedIds, $input); break;
        case 'retag':        $result = opRetag($db, $clientId, $userId, $allowedIds, $input); break;
        case 'recategorize': $result = opRecategorize($db, $clientId, $userId, $allowedIds, $input); break;
        case 'trash':        $result = opTrash($db, $clientId, $userId, $allowedIds); break;
        case 'restore':      $result = opRestore($db, $clientId, $userId, $allowedIds); break;
        default:
            dbnToolsError('Unknown op: ' . $op, 400, 'unknown_op');
    }
} catch (DbnToolsHttpException $e) {
    dbnToolsError($e->getMessage(), $e->status, $e->errorCode, $e->extra);
} catch (Throwable $e) {
    error_log('[dbn-dms/bulk] ' . $e->getMessage());
    dbnToolsError('Bulk operation failed.', 500, 'bulk_failed');
}

dbnToolsRespond($result);


function opMove(PDO $db, int $clientId, int $userId, string $tenantRole, array $ids, array $input): array
{
    $dest = $input['folder_id'] ?? null;
    $destId = ($dest === null || $dest === '' || $dest === 'unassigned' || (int)$dest === 0) ? null : (int)$dest;
    if ($destId !== null && !dbnDmsUserCanAccessFolder($destId, 'write', $clientId, $userId, $tenantRole)) {
        dbnToolsError('Forbidden on destination folder.', 403, 'forbidden_dest');
    }
    if ($destId !== null) {
        $check = $db->prepare('SELECT id FROM client_folders WHERE id = ? AND client_id = ? AND deleted_at IS NULL');
        $check->execute([$destId, $clientId]);
        if (!$check->fetchColumn()) {
            dbnToolsError('Destination folder not found.', 404, 'folder_not_found');
        }
    }
    $ph = implode(',', array_fill(0, count($ids), '?'));
    $stmt = $db->prepare("UPDATE client_documents SET folder_id = ?, updated_at = NOW() WHERE client_id = ? AND id IN ({$ph})");
    $stmt->execute(array_merge([$destId, $clientId], $ids));
    dbnDmsLogAudit($clientId, $userId ?: null, 'bulk_move', ['dest' => $destId, 'count' => count($ids), 'ids' => $ids]);
    return ['ok' => true, 'op' => 'move', 'affected' => $stmt->rowCount()];
}

function opRetag(PDO $db, int $clientId, int $userId, array $ids, array $input): array
{
    $mode = strtolower((string)($input['mode'] ?? 'replace'));
    if (!in_array($mode, ['replace','append','remove'], true)) {
        dbnToolsError('Invalid mode (replace|append|remove).', 422, 'invalid_mode');
    }
    $raw = (string)($input['tags'] ?? '');
    $newTags = array_values(array_filter(array_map('trim', explode(',', $raw))));
    $newTags = array_map(fn($t) => substr($t, 0, 32), $newTags);
    $newTags = array_slice($newTags, 0, 20);

    if ($mode === 'replace') {
        $ph = implode(',', array_fill(0, count($ids), '?'));
        $stmt = $db->prepare("UPDATE client_documents SET tags = ?, updated_at = NOW() WHERE client_id = ? AND id IN ({$ph})");
        $stmt->execute(array_merge([implode(',', $newTags), $clientId], $ids));
        $affected = $stmt->rowCount();
    } else {
        // Per-row merge.
        $affected = 0;
        $ph = implode(',', array_fill(0, count($ids), '?'));
        $cur = $db->prepare("SELECT id, tags FROM client_documents WHERE client_id = ? AND id IN ({$ph})");
        $cur->execute(array_merge([$clientId], $ids));
        $upd = $db->prepare("UPDATE client_documents SET tags = ?, updated_at = NOW() WHERE id = ? AND client_id = ?");
        foreach ($cur->fetchAll() as $row) {
            $existing = array_values(array_filter(array_map('trim', explode(',', (string)$row['tags']))));
            if ($mode === 'append') {
                $merged = array_values(array_unique(array_merge($existing, $newTags)));
            } else { // remove
                $merged = array_values(array_diff($existing, $newTags));
            }
            $merged = array_slice($merged, 0, 20);
            $upd->execute([implode(',', $merged), (int)$row['id'], $clientId]);
            $affected++;
        }
    }
    dbnDmsLogAudit($clientId, $userId ?: null, 'bulk_retag', ['mode' => $mode, 'tags' => $newTags, 'count' => count($ids), 'ids' => $ids]);
    return ['ok' => true, 'op' => 'retag', 'mode' => $mode, 'affected' => $affected];
}

function opRecategorize(PDO $db, int $clientId, int $userId, array $ids, array $input): array
{
    $cat = strtolower(trim((string)($input['category'] ?? '')));
    $cat = preg_replace('/[^a-z0-9\-_]/', '', $cat) ?: 'uncategorized';
    $cat = substr($cat, 0, 50);
    $ph  = implode(',', array_fill(0, count($ids), '?'));
    $stmt = $db->prepare("UPDATE client_documents SET category = ?, updated_at = NOW() WHERE client_id = ? AND id IN ({$ph})");
    $stmt->execute(array_merge([$cat, $clientId], $ids));
    dbnDmsLogAudit($clientId, $userId ?: null, 'bulk_recategorize', ['category' => $cat, 'count' => count($ids), 'ids' => $ids]);
    return ['ok' => true, 'op' => 'recategorize', 'category' => $cat, 'affected' => $stmt->rowCount()];
}

function opTrash(PDO $db, int $clientId, int $userId, array $ids): array
{
    $ph = implode(',', array_fill(0, count($ids), '?'));
    $stmt = $db->prepare(
        "UPDATE client_documents SET deleted_at = NOW(), deleted_by = ?, updated_at = NOW()
         WHERE client_id = ? AND id IN ({$ph}) AND deleted_at IS NULL"
    );
    $stmt->execute(array_merge([$userId ?: null, $clientId], $ids));
    dbnDmsLogAudit($clientId, $userId ?: null, 'bulk_trash', ['count' => count($ids), 'ids' => $ids]);
    return ['ok' => true, 'op' => 'trash', 'affected' => $stmt->rowCount()];
}

function opRestore(PDO $db, int $clientId, int $userId, array $ids): array
{
    $ph = implode(',', array_fill(0, count($ids), '?'));
    $stmt = $db->prepare(
        "UPDATE client_documents SET deleted_at = NULL, deleted_by = NULL, updated_at = NOW()
         WHERE client_id = ? AND id IN ({$ph}) AND deleted_at IS NOT NULL"
    );
    $stmt->execute(array_merge([$clientId], $ids));
    dbnDmsLogAudit($clientId, $userId ?: null, 'bulk_restore', ['count' => count($ids), 'ids' => $ids]);
    return ['ok' => true, 'op' => 'restore', 'affected' => $stmt->rowCount()];
}
