import { requireCampaignAccess, requireStageTwoUser, stageTwoApiError, stageTwoDatabase, stageTwoUuid } from '~/server/utils/stage-two-supabase' import { visibleProfile, type ProfileRecord } from '~/server/utils/profile' export default defineEventHandler(async (event) => { try { const user = await requireStageTwoUser(event) const campaignId = stageTwoUuid(getRouterParam(event, 'id'), 'campaign id') const access = await requireCampaignAccess(campaignId, user.id) const campaign = access.campaign const worldId = String(campaign.world_id) const [worlds, members, characters, openRounds, rounds] = await Promise.all([ stageTwoDatabase>>( `worlds?select=id,title,genre,tone,premise,content_boundaries,hook,opening_scene&id=eq.${worldId}&limit=1`, ), stageTwoDatabase>>( `campaign_members?select=id,user_id,role,active,ai_takeover_allowed,joined_at&campaign_id=eq.${campaignId}&order=joined_at.asc`, ), stageTwoDatabase>>( `characters?select=id,campaign_id,user_id,name,concept,controller,abilities,hp,max_hp,defense,proficiency,inventory,statuses,persona,rules_state,created_at&campaign_id=eq.${campaignId}&order=created_at.asc`, ), stageTwoDatabase>>( `rounds?select=id,campaign_id,number,status,forced_by,narration,next_prompt,queued_at,resolved_at,error,created_at&campaign_id=eq.${campaignId}&status=eq.open&order=number.desc&limit=1`, ), stageTwoDatabase>>( `rounds?select=id,campaign_id,number,status,forced_by,narration,next_prompt,queued_at,resolved_at,error,created_at&campaign_id=eq.${campaignId}&order=number.desc&limit=30`, ), ]) const fallbackRounds = openRounds.length ? [] : await stageTwoDatabase>>( `rounds?select=id,campaign_id,number,status,forced_by,narration,next_prompt,queued_at,resolved_at,error,created_at&campaign_id=eq.${campaignId}&order=number.desc&limit=1`, ) const round = openRounds[0] ?? fallbackRounds[0] ?? null const userIds = [...new Set(members.map(member => String(member.user_id)))] const profiles = userIds.length ? await stageTwoDatabase( `profiles?select=id,display_name,description,avatar_path,created_at,updated_at&id=in.(${userIds.join(',')})`, ) : [] const supabaseUrl = useRuntimeConfig().supabaseUrl const profileById = new Map(profiles.map(profile => [profile.id, visibleProfile(profile, supabaseUrl)])) const visibleMembers = members.map(member => ({ ...member, profile: profileById.get(String(member.user_id)) ?? null })) const visibleRounds = rounds.slice().reverse() const visibleRoundIds = visibleRounds.map(item => String(item.id)) const [intents, events, diceRolls] = await Promise.all([ round ? stageTwoDatabase>>( `player_intents?select=id,round_id,member_id,character_id,action,ready,created_at,updated_at&round_id=eq.${String(round.id)}&order=created_at.asc`, ) : Promise.resolve([]), stageTwoDatabase>>( `game_events?select=id,campaign_id,round_id,event_type,payload,created_at&campaign_id=eq.${campaignId}&order=created_at.desc&limit=50`, ), visibleRoundIds.length ? stageTwoDatabase>>( `dice_rolls?select=id,round_id,actor_id,target_id,check_kind,formula,rolls,kept,modifier,total,difficulty,success,created_at&round_id=in.(${visibleRoundIds.join(',')})&order=created_at.asc`, ) : Promise.resolve([]), ]) return { campaign, world: worlds[0] ?? null, members: visibleMembers, characters, round, rounds: visibleRounds, intents, events: events.slice().reverse(), diceRolls } } catch (error) { stageTwoApiError(error) } })