'Not authenticated']); exit; } function fetchOwnedCritter(PDO $pdo, int $userId, int $critterId): array|false { $stmt = $pdo->prepare(' SELECT c.id AS id, c.isFavorite AS isFavorite, c.isArchived AS isArchived, c.adjective_id AS adjective_id, c.noun_id AS noun_id, c.verb_id AS verb_id, c.location_id AS location_id, a.value AS adjective, n.value AS noun, v.value AS verb, l.value AS location, c.image AS image FROM critters c JOIN adjectives a ON a.id = c.adjective_id JOIN nouns n ON n.id = c.noun_id JOIN verbs v ON v.id = c.verb_id JOIN locations l ON l.id = c.location_id WHERE c.id = :id AND c.author_id = :author_id '); $stmt->execute(['id' => $critterId, 'author_id' => $userId]); return $stmt->fetch(PDO::FETCH_ASSOC); } $method = $_SERVER['REQUEST_METHOD']; $critterId = $method === 'POST' ? filter_input(INPUT_POST, 'id', FILTER_VALIDATE_INT) : filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT); if (!$critterId) { http_response_code(400); echo json_encode(['error' => 'Invalid critter id']); exit; } $critter = fetchOwnedCritter($pdo, (int) $_SESSION['user_id'], $critterId); if (!$critter) { http_response_code(404); echo json_encode(['error' => 'Critter not found']); exit; } if ($method === 'POST' && !csrf_verify($_POST['csrf_token'] ?? null)) { http_response_code(403); echo json_encode(['error' => 'Invalid CSRF token']); exit; } if ($method === 'POST') { $action = $_POST['action'] ?? ''; if ($action === 'toggle_favorite') { $newValue = $critter['isFavorite'] ? 0 : 1; $stmt = $pdo->prepare('UPDATE critters SET isFavorite = :value WHERE id = :id'); $stmt->execute(['value' => $newValue, 'id' => $critterId]); $critter['isFavorite'] = $newValue; } elseif ($action === 'toggle_archived') { $newValue = $critter['isArchived'] ? 0 : 1; $stmt = $pdo->prepare('UPDATE critters SET isArchived = :value WHERE id = :id'); $stmt->execute(['value' => $newValue, 'id' => $critterId]); $critter['isArchived'] = $newValue; } } $flourishes = [ 'Always up for a new adventure!', 'This one has a real personality.', 'Ready to make new friends.', "There's never a dull moment with this critter.", 'One of a kind, through and through.', 'A firm favorite in the collection.', 'Full of charm and curiosity.', ]; $flourish = $flourishes[array_rand($flourishes)]; $description = 'A ' . $critter['adjective'] . ' ' . $critter['noun'] . ' who ' . $critter['verb'] . ' ' . $critter['location'] . '. ' . $flourish; $prompt = "A {$critter['adjective']} {$critter['noun']} {$critter['verb']} {$critter['location']} in a high quality 3d animation art style. The image should be vibrant and colorful, capturing the essence of the action and setting."; $rarityTiers = [ ['label' => 'Common', 'classes' => 'bg-pink-100 text-pink-700'], ['label' => 'Rare', 'classes' => 'bg-blue-100 text-blue-700'], ['label' => 'Legendary', 'classes' => 'bg-amber-100 text-amber-700'], ]; $rarityRoll = $critter['id'] % 10; if ($rarityRoll >= 9) { $rarity = $rarityTiers[2]; } elseif ($rarityRoll >= 6) { $rarity = $rarityTiers[1]; } else { $rarity = $rarityTiers[0]; } $resolution = null; $fileSize = null; $createdAt = null; if (!empty($critter['image'])) { $imagePath = __DIR__ . '/../critter-images/' . $critter['image']; if (is_file($imagePath)) { $dimensions = @getimagesize($imagePath); if ($dimensions) { $resolution = $dimensions[0] . ' × ' . $dimensions[1]; } $bytes = @filesize($imagePath); if ($bytes !== false) { $fileSize = round($bytes / 1024) . ' KB'; } $mtime = @filemtime($imagePath); if ($mtime !== false) { $createdAt = date('j M Y, H:i', $mtime); } } } echo json_encode([ 'id' => (int) $critter['id'], 'title' => ucwords($critter['adjective'] . ' ' . $critter['noun']), 'adjective' => ucfirst($critter['adjective']), 'noun' => ucfirst($critter['noun']), 'verb' => ucfirst($critter['verb']), 'location' => ucfirst($critter['location']), 'isFavorite' => (bool) $critter['isFavorite'], 'isArchived' => (bool) $critter['isArchived'], 'rarity' => $rarity, 'description' => $description, 'prompt' => $prompt, 'createdAt' => $createdAt, 'resolution' => $resolution, 'fileSize' => $fileSize, 'imageUrl' => $critter['image'] ? 'critter-images/' . $critter['image'] : null, 'shareUrl' => 'single-critter.php?id=' . $critter['id'], 'createSimilarUrl' => 'create-critter.php?adjective_id=' . $critter['adjective_id'] . '&noun_id=' . $critter['noun_id'] . '&verb_id=' . $critter['verb_id'] . '&location_id=' . $critter['location_id'], ]);