import { createClient } from "https://esm.sh/@supabase/supabase-js@2.99.1"; const corsHeaders = { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type", }; Deno.serve(async (req) => { if (req.method === "OPTIONS") return new Response(null, { headers: corsHeaders }); try { const authHeader = req.headers.get("authorization") || ""; if (!authHeader.startsWith("Bearer ")) { return new Response(JSON.stringify({ error: "Unauthorized" }), { status: 401, headers: { ...corsHeaders, "Content-Type": "application/json" } }); } const supabaseUrl = Deno.env.get("SUPABASE_URL")!; const anonKey = Deno.env.get("SUPABASE_ANON_KEY")!; const serviceRoleKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!; const callerClient = createClient(supabaseUrl, anonKey, { global: { headers: { Authorization: authHeader } } }); const adminClient = createClient(supabaseUrl, serviceRoleKey); const { data: { user }, error: userError } = await callerClient.auth.getUser(); if (userError || !user?.id || !user.email) { return new Response(JSON.stringify({ error: "Unauthorized" }), { status: 401, headers: { ...corsHeaders, "Content-Type": "application/json" } }); } const normalizedEmail = user.email.trim().toLowerCase(); const displayName = (user.user_metadata?.full_name as string | undefined) || normalizedEmail; const { data: committees, error: committeeError } = await adminClient .from("arc_committee_members") .select("id, association_id, is_active") .or(`user_id.eq.${user.id},email.ilike.${normalizedEmail}`); if (committeeError) throw committeeError; // Ensure caller has arc_member role if they're on any committee roster if ((committees ?? []).some((c) => c.is_active)) { const { data: existingRole } = await adminClient .from("user_roles") .select("role") .eq("user_id", user.id) .eq("role", "arc_member") .maybeSingle(); if (!existingRole) { await adminClient.from("user_roles").insert({ user_id: user.id, role: "arc_member" }); } } const { data: owners, error: ownersError } = await adminClient .from("owners") .select("association_id, first_name, last_name") .eq("user_id", user.id) .neq("status", "archived"); if (ownersError) throw ownersError; const committeeAssociationIds = (committees ?? []).filter((c) => c.is_active).map((c) => c.association_id).filter(Boolean); const allCommitteeAssociationIds = (committees ?? []).map((c) => c.association_id).filter(Boolean); const ownerAssociationIds = (owners ?? []).map((o) => o.association_id).filter(Boolean); const inactiveRosterIds = (committees ?? []) .filter((c) => !c.is_active && ownerAssociationIds.includes(c.association_id)) .map((c) => c.id); const missingRosterAssociationIds = ownerAssociationIds.filter((id) => !allCommitteeAssociationIds.includes(id)); if (inactiveRosterIds.length > 0) { const { error: activateError } = await adminClient .from("arc_committee_members") .update({ is_active: true }) .in("id", inactiveRosterIds); if (activateError) throw activateError; } if (missingRosterAssociationIds.length > 0) { const ownerName = owners?.find((o) => o.first_name || o.last_name); const name = ownerName ? `${ownerName.first_name || ""} ${ownerName.last_name || ""}`.trim() : displayName; const rows = [...new Set(missingRosterAssociationIds)].map((association_id) => ({ association_id, name, email: normalizedEmail, is_active: true, role: "Member", })); const { error: insertError } = await adminClient.from("arc_committee_members").insert(rows); if (insertError) throw insertError; } const associationIds = [...new Set([...committeeAssociationIds, ...ownerAssociationIds])]; if (!associationIds.length) { return new Response(JSON.stringify({ applications: [], associationIds, noCommittee: true }), { headers: { ...corsHeaders, "Content-Type": "application/json" } }); } const { data: applications, error: appError } = await adminClient .from("arc_applications") .select("*, associations(name), units(unit_number, address), owners(first_name, last_name, property_address)") .in("association_id", associationIds) .order("created_at", { ascending: false }); if (appError) throw appError; return new Response(JSON.stringify({ applications: applications ?? [], associationIds, noCommittee: false }), { headers: { ...corsHeaders, "Content-Type": "application/json" }, }); } catch (error) { const message = error instanceof Error ? error.message : "Failed to load ARC reviews"; return new Response(JSON.stringify({ error: message }), { status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" } }); } });