<?php
/**
 * api-content.php — SUBSPACE Content Operations API
 *
 * Handles upload-first workflow and ownership enforcement:
 * - POST /upload     — File upload + media record creation
 * - POST /assign     — Assign content to component instances
 * - POST /unassign   — Remove content from instances
 * - POST /visibility — Toggle public/private
 * - GET /my-content  — List current user's content
 * - GET /instance/{id}/content — List content in an instance
 */

require_once __DIR__ . '/../citadel_2026/vendor/autoload.php';
require_once __DIR__ . '/../citadel_2026/citadel-permissions.php';
require_once __DIR__ . '/subspace-audit.php';

use Firebase\JWT\JWT;
use Firebase\JWT\Key;

// --- Config ---
$db_host = 'localhost';
$db_name = getenv('SUBSPACE_DB_NAME') ?: 'subspace_2026';
$db_user = getenv('SUBSPACE_DB_USER') ?: 'data';
$db_pass = getenv('SUBSPACE_DB_PASS') ?: 'ncc1701D';
$jwt_secret = getenv('CITADEL_2025_JWT_SECRET') ?: 'bXlzZWN1cmVzdHJpbmcxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMw==';
$storage_base = '/var/www/storage/subspace';
$hmac_secret = trim(file_get_contents($storage_base . '/.hmac-secret'));

// --- CORS ---
// Handled at Apache vhost level — do NOT set headers here.

// --- Database ---
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 (PDOException $e) {
    http_response_code(500);
    echo json_encode(['error' => 'Database connection failed']);
    exit;
}

// --- Auth helper ---
function getAuthPayload(string $jwt_secret): ?object {
    $auth = $_SERVER['HTTP_AUTHORIZATION'] ?? apache_request_headers()['Authorization'] ?? '';
    if (!preg_match('/Bearer\s(\S+)/', $auth, $m)) return null;
    try {
        return JWT::decode($m[1], new Key($jwt_secret, 'HS256'));
    } catch (\Exception $e) {
        return null;
    }
}

function requireAuth(string $jwt_secret): object {
    $payload = getAuthPayload($jwt_secret);
    if (!$payload) {
        http_response_code(401);
        echo json_encode(['error' => 'Authentication required']);
        exit;
    }
    return $payload;
}

function isAdmin(object $payload): bool {
    if (($payload->role ?? '') === 'super_admin') return true;
    if (isset($payload->perm) && is_array($payload->perm)) {
        return in_array('inkwell:settings:manage', $payload->perm, true);
    }
    return false;
}

function getUserId(object $payload): string {
    return (string)($payload->user_id ?? $payload->sub ?? '0');
}

// --- Routing ---
$method = $_SERVER['REQUEST_METHOD'];
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
// Detect API base path (subspace_2026, subspace_prod, etc.) for URL generation
preg_match('#^/(subspace_[a-z0-9]+)/#', $uri, $_base);
$api_base = '/' . ($_base[1] ?? 'subspace_2026');
// Strip /subspace_{env}/ prefix — supports both path-style (/upload) and query-style (?action=upload)
$path = preg_replace('#^/subspace_[a-z0-9]+/api-content\.php/?#', '', $uri);
$path = trim($path, '/');
if ($path === '' && isset($_GET['action'])) {
    $path = trim($_GET['action'], '/');
}

header('Content-Type: application/json');

// Handle OPTIONS preflight
if ($method === 'OPTIONS') {
    http_response_code(200);
    exit;
}

// --- Routes ---
if ($method === 'POST' && $path === 'upload') {
    $payload = requireAuth($jwt_secret);
    $user_id = getUserId($payload);

    if (empty($_FILES['file'])) {
        http_response_code(400);
        echo json_encode(['error' => 'No file uploaded']);
        exit;
    }

    $file = $_FILES['file'];
    $site_slug = $_POST['site'] ?? 'inkwell';
    $category = $_POST['category'] ?? 'media';

    // Validate category
    $valid_cats = ['gallery', 'artists', 'carousel', 'posts', 'media'];
    if (!in_array($category, $valid_cats)) $category = 'media';

    // Get site_id
    $stmt = $pdo->prepare('SELECT id FROM sites WHERE slug = ?');
    $stmt->execute([$site_slug]);
    $site = $stmt->fetch();
    if (!$site) {
        http_response_code(404);
        echo json_encode(['error' => 'Site not found']);
        exit;
    }

    // Generate UUID and slug
    $uuid = sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
        mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff),
        mt_rand(0, 0x0fff) | 0x4000, mt_rand(0, 0x3fff) | 0x8000,
        mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff));
    $ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
    $slug = pathinfo($file['name'], PATHINFO_FILENAME);
    $slug = preg_replace('/[^a-z0-9]+/i', '-', strtolower($slug));
    $slug = trim($slug, '-') . '-' . substr($uuid, 0, 8);

    // Generate HMAC signature
    $signature = substr(hash_hmac('sha256', $site['id'] . ':' . $uuid, $hmac_secret), 0, 16);

    // Store file
    $storage_dir = "$storage_base/sites/$site_slug/$category";
    $storage_path = "$storage_dir/$uuid.$ext";
    if (!move_uploaded_file($file['tmp_name'], $storage_path)) {
        http_response_code(500);
        echo json_encode(['error' => 'Failed to store file']);
        exit;
    }
    chmod($storage_path, 0640);

    // Get image dimensions if applicable
    $width = null;
    $height = null;
    if (str_starts_with($file['type'], 'image/')) {
        $dims = getimagesize($storage_path);
        if ($dims) { $width = $dims[0]; $height = $dims[1]; }
    }

    // Extract video thumbnail (first frame) via ffmpeg
    $thumbnail_url = null;
    if (str_starts_with($file['type'], 'video/')) {
        $thumb_uuid = sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
            mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff),
            mt_rand(0, 0x0fff) | 0x4000, mt_rand(0, 0x3fff) | 0x8000,
            mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff));
        $thumb_slug = $slug . '-thumb-' . substr($thumb_uuid, 0, 8);
        $thumb_sig = substr(hash_hmac('sha256', $site['id'] . ':' . $thumb_uuid, $hmac_secret), 0, 16);
        $thumb_path = "$storage_dir/$thumb_uuid.jpg";

        $cmd = sprintf('ffmpeg -i %s -vframes 1 -q:v 2 -y %s 2>&1',
            escapeshellarg($storage_path), escapeshellarg($thumb_path));
        exec($cmd, $output, $ret);

        if ($ret === 0 && file_exists($thumb_path)) {
            chmod($thumb_path, 0640);
            $thumb_size = filesize($thumb_path);
            $thumb_dims = getimagesize($thumb_path);
            $tw = $thumb_dims ? $thumb_dims[0] : null;
            $th = $thumb_dims ? $thumb_dims[1] : null;

            // Also use video dimensions for the main record
            $width = $tw;
            $height = $th;

            $member = subspace_member($pdo, $user_id); $artist_id = subspace_resolve_artist($pdo, $member, $payload->role ?? null, $_POST['artist_id'] ?? null);
    $stmt = $pdo->prepare('INSERT INTO media (artist_id, site_id, uuid, filename, slug, mime_type, size_bytes, width, height, storage_path, signature, uploaded_by, alt_text, caption)
                VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)');
            $stmt->execute([
                $artist_id, $site['id'], $thumb_uuid, $slug . '-thumb.jpg', $thumb_slug, 'image/jpeg',
                $thumb_size, $tw, $th, $thumb_path, $thumb_sig,
                $user_id, null, null
            ]);
            $thumbnail_url = "$api_base/img/p/$thumb_slug-$thumb_sig.jpg";
        }
    }

    // Insert media record
    $member = subspace_member($pdo, $user_id); $artist_id = subspace_resolve_artist($pdo, $member, $payload->role ?? null, $_POST['artist_id'] ?? null);
    $stmt = $pdo->prepare('INSERT INTO media (artist_id, site_id, uuid, filename, slug, mime_type, size_bytes, width, height, storage_path, signature, uploaded_by, alt_text, caption)
        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)');
    $stmt->execute([
        $artist_id, $site['id'], $uuid, $file['name'], $slug, $file['type'],
        $file['size'], $width, $height, $storage_path, $signature,
        $user_id, $_POST['alt_text'] ?? null, $_POST['caption'] ?? null
    ]);

    $media_id = $pdo->lastInsertId();
    subspace_audit($pdo, subspace_identity($payload), 'upload', 'media', $media_id, 200, ['site'=>$site_slug ?? null,'category'=>$category,'filename'=>$file['name'] ?? null,'artist_id'=>$artist_id], $member['label'] ?? null);
    $response = [
        'id' => (int)$media_id,
        'uuid' => $uuid,
        'slug' => $slug,
        'signature' => $signature,
        'public_url' => "$api_base/img/p/$slug-$signature.$ext",
    ];
    if ($thumbnail_url) {
        $response['thumbnail_url'] = $thumbnail_url;
    }
    echo json_encode($response);
    exit;
}

if ($method === 'POST' && $path === 'assign') {
    $payload = requireAuth($jwt_secret);
    $user_id = getUserId($payload);
    $admin = isAdmin($payload);
    $input = json_decode(file_get_contents('php://input'), true);

    $instance_id = (int)($input['instance_id'] ?? 0);
    $content_type = $input['content_type'] ?? '';
    $content_id = (int)($input['content_id'] ?? 0);

    if (!$instance_id || !$content_type || !$content_id) {
        http_response_code(400);
        echo json_encode(['error' => 'instance_id, content_type, and content_id required']);
        exit;
    }

    // NOTE: Instance permissions relaxed during dev — any authenticated user can assign.
    // TODO: Re-enable instance_permissions check when per-instance access control is needed.

    $stmt = $pdo->prepare('INSERT INTO instance_content (instance_id, content_type, content_id, sort_order, added_by) VALUES (?, ?, ?, ?, ?)');
    $sort = (int)($input['sort_order'] ?? 0);
    $stmt->execute([$instance_id, $content_type, $content_id, $sort, $user_id]);
    subspace_audit($pdo, subspace_identity($payload), 'instance_add', 'instance_content', $pdo->lastInsertId(), 200, ['instance_id'=>$instance_id,'content_type'=>$content_type,'content_id'=>$content_id]);

    echo json_encode(['id' => (int)$pdo->lastInsertId(), 'status' => 'assigned']);
    exit;
}

if ($method === 'POST' && $path === 'unassign') {
    $payload = requireAuth($jwt_secret);
    $user_id = getUserId($payload);
    $admin = isAdmin($payload);
    $input = json_decode(file_get_contents('php://input'), true);

    $id = (int)($input['id'] ?? 0);
    if (!$id) {
        http_response_code(400);
        echo json_encode(['error' => 'id required (instance_content.id)']);
        exit;
    }

    // Check ownership
    $stmt = $pdo->prepare('SELECT added_by FROM instance_content WHERE id = ?');
    $stmt->execute([$id]);
    $row = $stmt->fetch();
    if (!$row) {
        http_response_code(404);
        echo json_encode(['error' => 'Assignment not found']);
        exit;
    }
    // NOTE: Ownership check relaxed during dev — any authenticated user can unassign.
    // TODO: Re-enable when per-user ownership enforcement is needed.

    $pdo->prepare('DELETE FROM instance_content WHERE id = ?')->execute([$id]);
    subspace_audit($pdo, subspace_identity($payload), 'instance_remove', 'instance_content', $id, 200, null);
    echo json_encode(['status' => 'unassigned']);
    exit;
}

if ($method === 'POST' && $path === 'visibility') {
    $payload = requireAuth($jwt_secret);
    $user_id = getUserId($payload);
    $admin = isAdmin($payload);
    $input = json_decode(file_get_contents('php://input'), true);

    $content_type = $input['content_type'] ?? '';
    $content_id = (int)($input['content_id'] ?? 0);
    $visibility = $input['visibility'] ?? '';

    if (!$content_type || !$content_id || !in_array($visibility, ['public', 'private'])) {
        http_response_code(400);
        echo json_encode(['error' => 'content_type, content_id, and visibility (public|private) required']);
        exit;
    }

    // Validate table name to prevent SQL injection
    $allowed_tables = ['artists', 'gallery_items', 'posts', 'faqs', 'testimonials', 'carousel_slides', 'content_blocks', 'media'];
    if (!in_array($content_type, $allowed_tables)) {
        http_response_code(400);
        echo json_encode(['error' => 'Invalid content_type']);
        exit;
    }

    // Check ownership
    $stmt = $pdo->prepare("SELECT uploaded_by FROM `$content_type` WHERE id = ?");
    $stmt->execute([$content_id]);
    $row = $stmt->fetch();
    if (!$row) {
        http_response_code(404);
        echo json_encode(['error' => 'Content not found']);
        exit;
    }
    if (!$admin && $row['uploaded_by'] !== $user_id) {
        http_response_code(403);
        echo json_encode(['error' => 'Only the uploader or admin can change visibility']);
        exit;
    }

    $pdo->prepare("UPDATE `$content_type` SET visibility = ? WHERE id = ?")->execute([$visibility, $content_id]);
    subspace_audit($pdo, subspace_identity($payload), 'visibility', $content_type, $content_id, 200, ['visibility'=>$visibility]);
    echo json_encode(['status' => 'updated', 'visibility' => $visibility]);
    exit;
}

if ($method === 'GET' && $path === 'my-content') {
    $payload = requireAuth($jwt_secret);
    $user_id = getUserId($payload);

    $result = [];
    $tables = ['media', 'gallery_items', 'posts', 'carousel_slides', 'content_blocks'];
    foreach ($tables as $table) {
        $stmt = $pdo->prepare("SELECT id, site_id, visibility, created_at FROM `$table` WHERE uploaded_by = ? ORDER BY created_at DESC");
        $stmt->execute([$user_id]);
        $rows = $stmt->fetchAll();
        if ($rows) $result[$table] = $rows;
    }

    echo json_encode($result);
    exit;
}

if ($method === 'GET' && preg_match('#^instance/(\d+)/content$#', $path, $m)) {
    $payload = requireAuth($jwt_secret);
    $user_id = getUserId($payload);
    $admin = isAdmin($payload);
    $instance_id = (int)$m[1];

    $stmt = $pdo->prepare('SELECT ic.id, ic.content_type, ic.content_id, ic.sort_order, ic.added_by, ic.created_at
        FROM instance_content ic WHERE ic.instance_id = ? ORDER BY ic.sort_order');
    $stmt->execute([$instance_id]);
    $items = $stmt->fetchAll();

    // Enrich with visibility check — hide private content from non-owners/non-admins
    $allowed_tables = ['artists', 'gallery_items', 'posts', 'faqs', 'testimonials', 'carousel_slides', 'content_blocks', 'media'];
    $enriched = [];
    foreach ($items as $item) {
        if (!in_array($item['content_type'], $allowed_tables)) continue;
        $stmt2 = $pdo->prepare("SELECT visibility, uploaded_by FROM `{$item['content_type']}` WHERE id = ?");
        $stmt2->execute([$item['content_id']]);
        $content = $stmt2->fetch();
        if (!$content) continue;
        if ($content['visibility'] === 'private' && !$admin && $content['uploaded_by'] !== $user_id) continue;
        $item['visibility'] = $content['visibility'];
        $enriched[] = $item;
    }

    echo json_encode($enriched);
    exit;
}

// 404 fallback
http_response_code(404);
echo json_encode(['error' => 'Not found', 'path' => $path]);
