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, x-supabase-client-platform, x-supabase-client-platform-version, x-supabase-client-runtime, x-supabase-client-runtime-version",
};

function getAppOrigin(req: Request) {
  const origin = req.headers.get("origin");
  if (origin) return origin.replace(/\/$/, "");

  const referer = req.headers.get("referer");
  if (referer) {
    try {
      return new URL(referer).origin;
    } catch {
      // Ignore malformed referer values
    }
  }

  const forwardedHost = req.headers.get("x-forwarded-host");
  if (forwardedHost) {
    const forwardedProto = req.headers.get("x-forwarded-proto") || "https";
    return `${forwardedProto}://${forwardedHost}`;
  }

  return null;
}

function getRecoveryLinkOptions(req: Request) {
  // Always use production domain for reset links sent via email
  const productionOrigin = "https://avria.cloud";
  return { redirectTo: `${productionOrigin}/reset-password` };
}

function getInviteLinkOptions(req: Request) {
  const productionOrigin = "https://avria.cloud";
  return { redirectTo: `${productionOrigin}/reset-password?mode=invite` };
}

function escapeHtml(value: string) {
  return String(value)
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/\"/g, "&quot;")
    .replace(/'/g, "&#39;");
}

function buildActionEmailHtml({
  title,
  intro,
  buttonLabel,
  actionLink,
  customMessage,
}: {
  title: string;
  intro: string;
  buttonLabel: string;
  actionLink: string;
  customMessage?: string;
}) {
  const safeActionLink = escapeHtml(actionLink);
  const customMsgHtml = customMessage
    ? `<div style="background-color:#f8fafc;border:1px solid #e2e8f0;border-radius:6px;padding:16px;margin-bottom:20px;font-size:15px;color:#334155;line-height:1.6;">${escapeHtml(customMessage).replace(/\n/g, "<br>")}</div>`
    : "";

  return `
    <div style="font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Arial,sans-serif;max-width:600px;margin:0 auto;padding:24px;background:#ffffff;">
      <h2 style="color:#1e293b;margin:0 0 16px;font-size:24px;line-height:1.25;">${escapeHtml(title)}</h2>
      ${customMsgHtml}
      <p style="color:#334155;font-size:15px;line-height:1.6;margin:0 0 24px;">${escapeHtml(intro)}</p>
      <div style="text-align:center;margin:28px 0;">
        <a href="${safeActionLink}" style="background-color:#0f172a;color:#ffffff;padding:13px 28px;text-decoration:none;border-radius:6px;font-weight:700;display:inline-block;font-size:15px;line-height:1.2;">${escapeHtml(buttonLabel)}</a>
      </div>
      <p style="color:#475569;font-size:13px;line-height:1.6;margin:0 0 8px;">If the button does not open, copy and paste this secure link into your browser:</p>
      <p style="word-break:break-all;margin:0 0 24px;"><a href="${safeActionLink}" style="color:#0f172a;text-decoration:underline;font-size:13px;line-height:1.6;">${safeActionLink}</a></p>
      <p style="color:#64748b;font-size:13px;margin:0;">If you did not request this, you can safely ignore this email.</p>
    </div>
  `;
}

async function getVerifiedSender(adminClient: any) {
  const { data: senders, error } = await adminClient
    .from("email_senders")
    .select("id")
    .eq("is_active", true)
    .eq("verified", true)
    .order("is_default", { ascending: false })
    .order("updated_at", { ascending: false })
    .limit(1);

  if (error) throw error;

  return senders?.[0] ?? null;
}

async function sendManagedEmail({
  callerClient,
  senderId,
  recipient,
  subject,
  html,
}: {
  callerClient: any;
  senderId: string;
  recipient: string;
  subject: string;
  html: string;
}) {
  const { data, error } = await callerClient.functions.invoke("send-smtp-email", {
    body: {
      sender_id: senderId,
      recipient: [recipient],
      subject,
      body: html,
      html,
      debug: false,
    },
  });

  if (error) throw error;
  if (data && typeof data === "object" && "success" in data && data.success === false) {
    throw new Error((data as { error?: string }).error || "Email send failed");
  }
}

function throwIfDbError(error: { message?: string } | null, context: string) {
  if (error) throw new Error(`${context}: ${error.message || "Database operation failed"}`);
}

async function syncLegalAssociationAssignments(
  adminClient: any,
  userId: string,
  assocIds: string[],
  callerId: string,
) {
  const { error: deleteError } = await adminClient
    .from("legal_association_assignments")
    .delete()
    .eq("user_id", userId);
  throwIfDbError(deleteError, "Could not clear existing legal association assignments");

  if (assocIds.length === 0) return;

  const rows = assocIds.map((aid: string) => ({
    user_id: userId,
    association_id: aid,
    created_by: callerId,
  }));

  const { error: insertError } = await adminClient
    .from("legal_association_assignments")
    .upsert(rows, { onConflict: "user_id,association_id" });
  throwIfDbError(insertError, "Could not save legal association assignments");
}

async function syncMasterBoardAssignments(
  adminClient: any,
  userId: string,
  assocIds: string[],
  callerId: string,
) {
  const { error: deleteError } = await adminClient
    .from("master_board_assignments")
    .delete()
    .eq("user_id", userId);
  throwIfDbError(deleteError, "Could not clear existing master board assignments");

  if (assocIds.length === 0) return;

  const rows = assocIds.map((aid: string) => ({
    user_id: userId,
    association_id: aid,
    created_by: callerId,
  }));

  const { error: insertError } = await adminClient
    .from("master_board_assignments")
    .upsert(rows, { onConflict: "user_id,association_id" });
  throwIfDbError(insertError, "Could not save master board assignments");
}

async function listAllAuthUsers(adminClient: any) {
  let allAuthUsers: any[] = [];
  let page = 1;
  while (true) {
    const { data, error } = await adminClient.auth.admin.listUsers({ page, perPage: 1000 });
    if (error) throw error;
    allAuthUsers = allAuthUsers.concat(data.users || []);
    if ((data.users || []).length < 1000) break;
    page++;
  }
  return allAuthUsers;
}

async function buildManagedUsers(adminClient: any) {
  const [authUsers, profilesRes, rolesRes, ownersRes, boardMembersRes, legalAssignmentsRes, masterBoardAssignmentsRes] = await Promise.all([
    listAllAuthUsers(adminClient),
    adminClient.from("profiles").select("*"),
    adminClient.from("user_roles").select("*"),
    adminClient.from("owners").select("id, first_name, last_name, user_id, unit_id, association_id").eq("status", "active").not("user_id", "is", null),
    adminClient.from("board_members").select("user_id, association_id, approval_authority").not("user_id", "is", null),
    adminClient.from("legal_association_assignments").select("user_id, association_id"),
    adminClient.from("master_board_assignments").select("user_id, association_id"),
  ]);

  throwIfDbError(profilesRes.error, "Could not load profiles");
  throwIfDbError(rolesRes.error, "Could not load user roles");
  throwIfDbError(ownersRes.error, "Could not load owner links");
  throwIfDbError(boardMembersRes.error, "Could not load board members");
  throwIfDbError(legalAssignmentsRes.error, "Could not load legal assignments");
  throwIfDbError(masterBoardAssignmentsRes.error, "Could not load master board assignments");

  const roleMap = new Map<string, string[]>();
  (rolesRes.data || []).forEach((r: any) => {
    if (!roleMap.has(r.user_id)) roleMap.set(r.user_id, []);
    roleMap.get(r.user_id)!.push(r.role);
  });

  const ownerUnitMap = new Map<string, string[]>();
  (ownersRes.data || []).forEach((o: any) => {
    if (!o.user_id || !o.unit_id) return;
    const existing = ownerUnitMap.get(o.user_id) || [];
    if (!existing.includes(o.unit_id)) existing.push(o.unit_id);
    ownerUnitMap.set(o.user_id, existing);
  });

  const boardMemberMap = new Map<string, { association_ids: string[]; approval_authority: boolean }>();
  (boardMembersRes.data || []).forEach((bm: any) => {
    if (!bm.user_id) return;
    const existing = boardMemberMap.get(bm.user_id) || { association_ids: [], approval_authority: false };
    if (!existing.association_ids.includes(bm.association_id)) existing.association_ids.push(bm.association_id);
    if (bm.approval_authority) existing.approval_authority = true;
    boardMemberMap.set(bm.user_id, existing);
  });

  const toAssignmentMap = (rows: any[] = []) => {
    const map = new Map<string, string[]>();
    rows.forEach((assignment: any) => {
      const existing = map.get(assignment.user_id) || [];
      if (!existing.includes(assignment.association_id)) existing.push(assignment.association_id);
      map.set(assignment.user_id, existing);
    });
    return map;
  };

  const legalAssignmentMap = toAssignmentMap(legalAssignmentsRes.data || []);
  const masterBoardAssignmentMap = toAssignmentMap(masterBoardAssignmentsRes.data || []);
  const profileMap = new Map((profilesRes.data || []).map((p: any) => [p.user_id, p]));
  const usersById = new Map<string, any>();
  for (const authUser of authUsers) {
    const profile: any = profileMap.get(authUser.id) || {};
    usersById.set(authUser.id, {
      user_id: authUser.id,
      full_name: profile.full_name || authUser.user_metadata?.full_name || authUser.user_metadata?.name || null,
      email: profile.email || authUser.email || "",
      phone: profile.phone || authUser.phone || "",
      is_blocked: profile.is_blocked || false,
      created_at: profile.created_at || authUser.created_at,
      roles: roleMap.get(authUser.id) || [],
      linked_unit_ids: ownerUnitMap.get(authUser.id) || [],
      board_member_association_ids: boardMemberMap.get(authUser.id)?.association_ids || [],
      legal_association_ids: legalAssignmentMap.get(authUser.id) || [],
      master_board_association_ids: masterBoardAssignmentMap.get(authUser.id) || [],
      is_bill_approver: boardMemberMap.get(authUser.id)?.approval_authority || false,
      last_sign_in_at: authUser.last_sign_in_at || null,
      auth_email: authUser.email || "",
      primary_owner_id: profile.primary_owner_id || null,
      primary_association_id: profile.primary_association_id || null,
      profile_missing: !profileMap.has(authUser.id),
    });
  }

  for (const profile of profilesRes.data || []) {
    if (usersById.has(profile.user_id)) continue;
    usersById.set(profile.user_id, {
      user_id: profile.user_id,
      full_name: profile.full_name || null,
      email: profile.email || "",
      phone: profile.phone || "",
      is_blocked: profile.is_blocked || false,
      created_at: profile.created_at,
      roles: roleMap.get(profile.user_id) || [],
      linked_unit_ids: ownerUnitMap.get(profile.user_id) || [],
      board_member_association_ids: boardMemberMap.get(profile.user_id)?.association_ids || [],
      legal_association_ids: legalAssignmentMap.get(profile.user_id) || [],
      master_board_association_ids: masterBoardAssignmentMap.get(profile.user_id) || [],
      is_bill_approver: boardMemberMap.get(profile.user_id)?.approval_authority || false,
      last_sign_in_at: null,
      primary_owner_id: profile.primary_owner_id || null,
      primary_association_id: profile.primary_association_id || null,
    });
  }

  return Array.from(usersById.values()).sort((a, b) => String(b.created_at || "").localeCompare(String(a.created_at || "")));
}


Deno.serve(async (req) => {
  if (req.method === "OPTIONS") {
    return new Response(null, { headers: corsHeaders });
  }

  try {
    // Verify the caller
    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 serviceRoleKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!;
    const anonKey = Deno.env.get("SUPABASE_ANON_KEY")!;

    // Client with caller's JWT to check role
    const callerClient = createClient(supabaseUrl, anonKey, {
      global: { headers: { Authorization: authHeader } },
    });

    const token = authHeader.replace("Bearer ", "");
    const {
      data: claimsData,
      error: callerError,
    } = await callerClient.auth.getClaims(token);

    const callerId = claimsData?.claims?.sub;
    if (callerError || !callerId) {
      return new Response(JSON.stringify({ error: "Unauthorized" }), {
        status: 401,
        headers: { ...corsHeaders, "Content-Type": "application/json" },
      });
    }

    // Check if caller has User Management permission.
    // Admins/managers pass automatically — verified directly here so the function
    // still works when has_feature_permission is missing or errors.
    const adminClientForRoles = createClient(supabaseUrl, serviceRoleKey);
    const { data: callerRolesData } = await adminClientForRoles
      .from("user_roles")
      .select("role")
      .eq("user_id", callerId);
    const callerRoles = (callerRolesData || []).map((r: any) => r.role);
    const isAdminOrManager = callerRoles.includes("admin") || callerRoles.includes("manager");

    let hasReadAccess: boolean = isAdminOrManager;
    if (!isAdminOrManager) {
      const { data: rpcData } = await callerClient.rpc("has_feature_permission", {
        _user_id: callerId,
        _feature_area: "User Management",
        _action: "read",
      });
      hasReadAccess = !!rpcData;
    }
    if (!hasReadAccess) {
      return new Response(JSON.stringify({ error: "User Management access required" }), {
        status: 403,
        headers: { ...corsHeaders, "Content-Type": "application/json" },
      });
    }

    // Helper to check specific action permissions
    const checkPermission = async (action: string) => {
      if (isAdminOrManager) return true;
      const { data: allowed } = await callerClient.rpc("has_feature_permission", {
        _user_id: callerId,
        _feature_area: "User Management",
        _action: action,
      });
      return !!allowed;
    };

    // Admin client with service role for user management
    const adminClient = createClient(supabaseUrl, serviceRoleKey, {
      auth: { autoRefreshToken: false, persistSession: false },
    });

    const body = await req.json();
    const { action } = body;
    const recoveryLinkOptions = getRecoveryLinkOptions(req);
    const inviteLinkOptions = getInviteLinkOptions(req);

    let result: any;

    switch (action) {
      case "list_users": {
        const { data, error } = await adminClient.auth.admin.listUsers({
          page: body.page || 1,
          perPage: body.perPage || 100,
        });
        if (error) throw error;
        result = data;
        break;
      }

      case "list_managed_users": {
        result = { users: await buildManagedUsers(adminClient) };
        break;
      }

      case "create_user": {
        if (!(await checkPermission("create"))) throw new Error("Create permission denied");
        const { password, full_name, role, owner_id, unit_ids, association_id, association_ids, is_board_member, is_arc_member, is_fining_committee, is_bill_approver } = body;
        const email = typeof body.email === "string" ? body.email.trim().toLowerCase() : "";
        if (!email || !password) throw new Error("Email and password required");
        const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
        if (!emailRegex.test(email)) {
          throw new Error(`Invalid email format: "${email}"`);
        }

        // Normalize to array, supporting both association_id (legacy) and association_ids (new)
        const assocIds: string[] = Array.isArray(association_ids) && association_ids.length > 0
          ? association_ids
          : (association_id ? [association_id] : []);

        // Create auth user
        const { data: newUser, error: createError } =
          await adminClient.auth.admin.createUser({
            email,
            password,
            email_confirm: true,
            user_metadata: { full_name: full_name || "" },
          });
        if (createError) throw createError;

        const userId = newUser.user.id;

        // Ensure profile exists (trigger should create it, but update name/email)
        await adminClient
          .from("profiles")
          .update({ full_name: full_name || "", email })
          .eq("user_id", userId);

        // Set primary role
        if (role) {
          await adminClient
            .from("user_roles")
            .delete()
            .eq("user_id", userId);
          await adminClient
            .from("user_roles")
            .insert({ user_id: userId, role });
        }

        // Add additional committee roles
        const additionalRoles: string[] = [];
        if (is_board_member && role !== "board_member") additionalRoles.push("board_member");
        if (is_arc_member && role !== "arc_member") additionalRoles.push("arc_member");
        if (is_fining_committee && role !== "fining_member") additionalRoles.push("fining_member");
        if (additionalRoles.length > 0) {
          await adminClient
            .from("user_roles")
            .insert(additionalRoles.map((r) => ({ user_id: userId, role: r })));
        }

        // Link to owner if provided, or auto-link by email/name match using first association
        const primaryAssocId = assocIds[0] || null;
        if (role !== "legal" && owner_id) {
          await adminClient
            .from("owners")
            .update({ user_id: userId })
            .eq("id", owner_id);
        } else if (role !== "legal" && primaryAssocId && email) {
          // Auto-link: find unlinked owner in same association by email match
          const { data: emailMatch } = await adminClient
            .from("owners")
            .select("id")
            .eq("association_id", primaryAssocId)
            .eq("status", "active")
            .is("user_id", null)
            .eq("email", email)
            .limit(1);

          if (emailMatch && emailMatch.length > 0) {
            await adminClient
              .from("owners")
              .update({ user_id: userId })
              .eq("id", emailMatch[0].id);
          } else if (full_name) {
            // Try matching by name (first_name + last_name contains full_name)
            const nameParts = (full_name as string).trim().split(/\s+/);
            const firstName = nameParts[0] || "";
            const lastName = nameParts.slice(1).join(" ") || "";
            
            let query = adminClient
              .from("owners")
              .select("id")
              .eq("association_id", primaryAssocId)
              .eq("status", "active")
              .is("user_id", null);
            
            if (firstName) query = query.ilike("first_name", `%${firstName}%`);
            if (lastName) query = query.ilike("last_name", `%${lastName}%`);
            
            const { data: nameMatch } = await query.limit(1);
            if (nameMatch && nameMatch.length > 0) {
              await adminClient
                .from("owners")
                .update({ user_id: userId })
                .eq("id", nameMatch[0].id);
            }
          }
        }

        // Association-only roles (legal, master_board_member): tag communities, no unit links.
        if (role === "legal") {
          await syncLegalAssociationAssignments(adminClient, userId, assocIds, callerId);
        } else if (role === "master_board_member") {
          await syncMasterBoardAssignments(adminClient, userId, assocIds, callerId);
        } else if (Array.isArray(unit_ids) && unit_ids.length > 0) {
          for (const uid of unit_ids) {
            await adminClient
              .from("owners")
              .update({ user_id: userId })
              .eq("unit_id", uid)
              .is("user_id", null);
          }
        }

        // Create board_members records for all assigned associations
        if ((role === "board_member" || is_board_member) && assocIds.length > 0) {
          const boardMemberRows = assocIds.map((aid: string) => ({
            association_id: aid,
            member_name: full_name || email,
            member_email: email,
            user_id: userId,
            role: "Member",
            approval_authority: is_bill_approver || false,
          }));
          await adminClient.from("board_members").insert(boardMemberRows);
        }

        // Create arc_committee_members records for all assigned associations when ARC member
        if ((role === "arc_member" || is_arc_member) && assocIds.length > 0) {
          // Remove any existing rows for this email (avoid duplicates from manual entries)
          await adminClient.from("arc_committee_members").delete().ilike("email", email);
          const arcRows = assocIds.map((aid: string) => ({
            association_id: aid,
            name: full_name || email,
            email,
            is_active: true,
          }));
          await adminClient.from("arc_committee_members").insert(arcRows);
        }

        result = { user: newUser.user };
        break;
      }

      case "update_user": {
        if (!(await checkPermission("edit"))) throw new Error("Edit permission denied");
        const { user_id, email, full_name, role, owner_id, unit_ids, phone, association_id, association_ids, is_board_member, is_arc_member, is_fining_committee, is_bill_approver } = body;
        if (!user_id) throw new Error("user_id required");

        // Normalize to array
        const assocIds: string[] = Array.isArray(association_ids) && association_ids.length > 0
          ? association_ids
          : (association_id ? [association_id] : []);

        // Update auth email if changed
        if (email) {
          const { error } = await adminClient.auth.admin.updateUserById(
            user_id,
            { email, email_confirm: true }
          );
          if (error) throw error;
        }

        // Update profile
        const profileUpdate: any = {};
        if (full_name !== undefined) profileUpdate.full_name = full_name;
        if (email !== undefined) profileUpdate.email = email;
        if (phone !== undefined) profileUpdate.phone = phone;
        if (Object.keys(profileUpdate).length > 0) {
          await adminClient
            .from("profiles")
            .update(profileUpdate)
            .eq("user_id", user_id);
        }

        // Update roles: set primary + additional committee roles
        if (role) {
          await adminClient
            .from("user_roles")
            .delete()
            .eq("user_id", user_id);
          
          const rolesToInsert: { user_id: string; role: string }[] = [
            { user_id, role },
          ];
          if (is_board_member && role !== "board_member") rolesToInsert.push({ user_id, role: "board_member" });
          if (is_arc_member && role !== "arc_member") rolesToInsert.push({ user_id, role: "arc_member" });
          if (is_fining_committee && role !== "fining_member") rolesToInsert.push({ user_id, role: "fining_member" });
          
          await adminClient
            .from("user_roles")
            .insert(rolesToInsert);
        }

        // Sync community assignments. Legal/master_board are association-only; otherwise link by unit_ids/owner_id.
        if (role === "legal") {
          await adminClient
            .from("owners")
            .update({ user_id: null })
            .eq("user_id", user_id);
          await syncMasterBoardAssignments(adminClient, user_id, [], callerId);
          await syncLegalAssociationAssignments(adminClient, user_id, assocIds, callerId);
        } else if (role === "master_board_member") {
          await adminClient
            .from("owners")
            .update({ user_id: null })
            .eq("user_id", user_id);
          await syncLegalAssociationAssignments(adminClient, user_id, [], callerId);
          await syncMasterBoardAssignments(adminClient, user_id, assocIds, callerId);
        } else if (Array.isArray(unit_ids)) {
          await syncLegalAssociationAssignments(adminClient, user_id, [], callerId);
          await syncMasterBoardAssignments(adminClient, user_id, [], callerId);
          // Unlink all existing owners for this user
          await adminClient
            .from("owners")
            .update({ user_id: null })
            .eq("user_id", user_id);
          // Link owners for the specified units
          for (const uid of unit_ids) {
            await adminClient
              .from("owners")
              .update({ user_id: user_id })
              .eq("unit_id", uid);
          }
        } else if (owner_id !== undefined) {
          await adminClient
            .from("owners")
            .update({ user_id: null })
            .eq("user_id", user_id);
          if (owner_id) {
            await adminClient
              .from("owners")
              .update({ user_id: user_id })
              .eq("id", owner_id);
          }
        }

        // Sync board_members records for all associations
        // Remove all existing board_members for this user
        await adminClient
          .from("board_members")
          .delete()
          .eq("user_id", user_id);

        // Re-create for each assigned association if board_member role or flag
        if ((role === "board_member" || is_board_member) && assocIds.length > 0) {
          const boardMemberRows = assocIds.map((aid: string) => ({
            association_id: aid,
            member_name: full_name || email || "",
            member_email: email || "",
            user_id,
            role: "Member",
            approval_authority: is_bill_approver || false,
          }));
          await adminClient.from("board_members").insert(boardMemberRows);
        }

        // Sync arc_committee_members. Always clear existing rows for this user's email,
        // then re-create when the user is an ARC member for each assigned association.
        if (email) {
          await adminClient.from("arc_committee_members").delete().ilike("email", email);
        }
        if ((role === "arc_member" || is_arc_member) && assocIds.length > 0 && email) {
          const arcRows = assocIds.map((aid: string) => ({
            association_id: aid,
            name: full_name || email,
            email,
            is_active: true,
          }));
          await adminClient.from("arc_committee_members").insert(arcRows);
        }

        result = { success: true };
        break;
      }

      case "delete_user": {
        if (!(await checkPermission("delete"))) throw new Error("Delete permission denied");
        const { user_id } = body;
        if (!user_id) throw new Error("user_id required");

        // Unlink from owners and board_members before deleting auth user
        await adminClient
          .from("owners")
          .update({ user_id: null })
          .eq("user_id", user_id);

        await adminClient
          .from("board_members")
          .update({ user_id: null })
          .eq("user_id", user_id);

        await adminClient
          .from("legal_association_assignments")
          .delete()
          .eq("user_id", user_id);

        await adminClient
          .from("master_board_assignments")
          .delete()
          .eq("user_id", user_id);

        // Delete auth user (cascades to profiles and user_roles)
        const { error } = await adminClient.auth.admin.deleteUser(user_id);
        if (error) throw error;

        result = { success: true };
        break;
      }

      case "bulk_delete_users": {
        if (!(await checkPermission("delete"))) throw new Error("Delete permission denied");
        const { user_ids } = body;
        if (!Array.isArray(user_ids) || user_ids.length === 0) throw new Error("user_ids array required");

        const results: { deleted: number; errors: string[] } = { deleted: 0, errors: [] };

        for (const uid of user_ids) {
          try {
            // Unlink from owners and board_members
            await adminClient
              .from("owners")
              .update({ user_id: null })
              .eq("user_id", uid);

            await adminClient
              .from("board_members")
              .update({ user_id: null })
              .eq("user_id", uid);

            await adminClient
              .from("legal_association_assignments")
              .delete()
              .eq("user_id", uid);

            await adminClient
              .from("master_board_assignments")
              .delete()
              .eq("user_id", uid);

            // Delete auth user (cascades to profiles and user_roles)
            const { error: delErr } = await adminClient.auth.admin.deleteUser(uid);
            if (delErr) throw delErr;
            results.deleted++;
          } catch (e: any) {
            results.errors.push(`${uid}: ${e.message}`);
          }
        }

        result = results;
        break;
      }

      case "bulk_update_users": {
        if (!(await checkPermission("edit"))) throw new Error("Edit permission denied");
        const { user_ids, role, association_id, blocked } = body;
        if (!Array.isArray(user_ids) || user_ids.length === 0) throw new Error("user_ids array required");

        const results: { updated: number; errors: string[] } = { updated: 0, errors: [] };

        for (const uid of user_ids) {
          try {
            // Update role if provided
            if (role) {
              await adminClient.from("user_roles").delete().eq("user_id", uid);
              await adminClient.from("user_roles").insert({ user_id: uid, role });
            }

            // Update association link if provided.
            if (association_id !== undefined) {
              await adminClient.from("board_members").delete().eq("user_id", uid);
              await adminClient.from("legal_association_assignments").delete().eq("user_id", uid);
              await adminClient.from("master_board_assignments").delete().eq("user_id", uid);

              if (association_id) {
                // Get user's profile for name/email
                const { data: profile } = await adminClient
                  .from("profiles")
                  .select("full_name, email")
                  .eq("user_id", uid)
                  .single();

                if (role === "legal") {
                  await adminClient.from("legal_association_assignments").insert({ user_id: uid, association_id, created_by: callerId });
                } else if (role === "master_board_member") {
                  await adminClient.from("master_board_assignments").insert({ user_id: uid, association_id, created_by: callerId });
                } else {
                  await adminClient.from("board_members").insert({
                    association_id,
                    member_name: profile?.full_name || profile?.email || "",
                    member_email: profile?.email || "",
                    user_id: uid,
                    role: "Member",
                  });
                }
              }
            }

            // Update blocked status if provided
            if (blocked !== undefined) {
              await adminClient
                .from("profiles")
                .update({ is_blocked: !!blocked })
                .eq("user_id", uid);

              await adminClient.auth.admin.updateUserById(uid, {
                ban_duration: blocked ? "876600h" : "none",
              });
            }

            results.updated++;
          } catch (e: any) {
            results.errors.push(`${uid}: ${e.message}`);
          }
        }

        result = results;
        break;
      }

      case "sign_in_as_user": {
        // Only admins can impersonate
        const { data: isAdmin } = await callerClient.rpc("has_role", {
          _user_id: callerId,
          _role: "admin",
        });
        if (!isAdmin) throw new Error("Only admins can sign in as another user");

        const { user_id } = body;
        if (!user_id) throw new Error("user_id required");

        // Generate a magic link for the target user
        const { data: userData, error: userError } = await adminClient.auth.admin.getUserById(user_id);
        if (userError || !userData?.user?.email) throw new Error("Could not find user email");

        const { data: linkData, error: linkError } = await adminClient.auth.admin.generateLink({
          type: "magiclink",
          email: userData.user.email,
        });
        if (linkError) throw linkError;

        // Return the token hash and verification type so the client can exchange it
        result = {
          success: true,
          token_hash: linkData?.properties?.hashed_token,
          email: userData.user.email,
        };
        break;
      }

      case "reset_password": {
        const { user_id, new_password } = body;
        if (!user_id || !new_password)
          throw new Error("user_id and new_password required");

        const { error } = await adminClient.auth.admin.updateUserById(
          user_id,
          { password: new_password }
        );
        if (error) throw error;

        result = { success: true };
        break;
      }

      case "block_user": {
        const { user_id, blocked } = body;
        if (!user_id) throw new Error("user_id required");

        // Update profile blocked status
        await adminClient
          .from("profiles")
          .update({ is_blocked: !!blocked })
          .eq("user_id", user_id);

        // Ban/unban in auth
        const { error } = await adminClient.auth.admin.updateUserById(
          user_id,
          { ban_duration: blocked ? "876600h" : "none" }
        );
        if (error) throw error;

        result = { success: true, blocked: !!blocked };
        break;
      }

      case "send_reset_email": {
        const { email, custom_message } = body;
        if (!email) throw new Error("email required");

        const { data: linkData, error: linkError } =
          await adminClient.auth.admin.generateLink({
            type: "recovery",
            email,
            ...(recoveryLinkOptions ? { options: recoveryLinkOptions } : {}),
          });
        if (linkError) throw linkError;

        const resetLink = linkData?.properties?.action_link || "";

        if (!resetLink) throw new Error("Could not generate reset link");

        const sender = await getVerifiedSender(adminClient);
        if (!sender?.id) throw new Error("No verified email sender is configured");

        const htmlBody = buildActionEmailHtml({
          title: "Password Reset",
          intro: "Click the button below to reset your password:",
          buttonLabel: "Reset Password",
          actionLink: resetLink,
          customMessage: custom_message,
        });

        await sendManagedEmail({
          callerClient,
          senderId: sender.id,
          recipient: email,
          subject: "Password Reset Request",
          html: htmlBody,
        });

        result = { success: true, message: "Password reset link sent" };
        break;
      }

      case "invite_user": {
        if (!(await checkPermission("create"))) throw new Error("Create permission denied");
        const { email } = body;
        if (!email) throw new Error("email required");

        // Try invite first; if user already exists, send a recovery link instead
        const { data, error } = await adminClient.auth.admin.inviteUserByEmail(email, inviteLinkOptions);
        
        if (error && error.message?.includes("already been registered")) {
          // User exists — generate a recovery/welcome link and send via SMTP
          const { data: linkData, error: linkError } = await adminClient.auth.admin.generateLink({
            type: "recovery",
            email,
            ...(recoveryLinkOptions ? { options: recoveryLinkOptions } : {}),
          });
          if (linkError) throw linkError;

          const resetLink = linkData?.properties?.action_link || "";

          if (!resetLink) throw new Error("Could not generate invitation link");

          const sender = await getVerifiedSender(adminClient);
          if (!sender?.id) throw new Error("No verified email sender is configured");

          const htmlBody = buildActionEmailHtml({
            title: "You've Been Invited",
            intro: "You have been invited to access the system. Click the button below to set up your password and get started:",
            buttonLabel: "Set Up Password",
            actionLink: resetLink,
          });

          await sendManagedEmail({
            callerClient,
            senderId: sender.id,
            recipient: email,
            subject: "You've Been Invited",
            html: htmlBody,
          });

          result = { success: true, message: `Invitation sent to ${email}` };
          break;
        }

        if (error) throw error;

        // New user — ensure profile + default role exist
        if (data?.user) {
          const { data: existingProfile } = await adminClient.from("profiles").select("user_id").eq("user_id", data.user.id).maybeSingle();
          if (!existingProfile) {
            await adminClient.from("profiles").insert({ user_id: data.user.id, full_name: "", email });
          }
          const { data: existingRole } = await adminClient.from("user_roles").select("id").eq("user_id", data.user.id).limit(1);
          if (!existingRole?.length) {
            await adminClient.from("user_roles").insert({ user_id: data.user.id, role: "homeowner" });
          }
        }

        result = { success: true, message: `Invitation sent to ${email}` };
        break;
      }

      case "bulk_send_reset_email": {
        const { user_ids } = body;
        if (!user_ids?.length) throw new Error("user_ids required");

        const results: { sent: number; errors: string[] } = { sent: 0, errors: [] };
        const sender = await getVerifiedSender(adminClient);

        if (!sender?.id) throw new Error("No verified email sender is configured");

        for (const uid of user_ids) {
          try {
            // Get user email
            const { data: userData, error: userErr } = await adminClient.auth.admin.getUserById(uid);
            if (userErr || !userData?.user?.email) {
              results.errors.push(`${uid}: user not found`);
              continue;
            }

            const { data: linkData, error } = await adminClient.auth.admin.generateLink({
              type: "recovery",
              email: userData.user.email,
              ...(recoveryLinkOptions ? { options: recoveryLinkOptions } : {}),
            });
            if (error) throw error;

            const resetLink = linkData?.properties?.action_link || "";
            if (!resetLink) throw new Error("Could not generate reset link");

            const htmlBody = buildActionEmailHtml({
              title: "Password Reset",
              intro: "Click the button below to reset your password:",
              buttonLabel: "Reset Password",
              actionLink: resetLink,
            });

            await sendManagedEmail({
              callerClient,
              senderId: sender.id,
              recipient: userData.user.email,
              subject: "Password Reset Request",
              html: htmlBody,
            });

            results.sent++;
          } catch (e: any) {
            results.errors.push(`${uid}: ${e.message}`);
          }
        }

        result = results;
        break;
      }

      case "bulk_invite_users": {
        if (!(await checkPermission("create"))) throw new Error("Create permission denied");
        const { emails } = body;
        if (!emails?.length) throw new Error("emails array required");

        const results: { sent: number; errors: string[] } = { sent: 0, errors: [] };
        const uniqueEmails: string[] = Array.from(new Set(
          (emails as unknown[]).map((email) => String(email || "").trim().toLowerCase()).filter(Boolean)
        )) as string[];

        for (const email of uniqueEmails) {
          try {
            const { data, error } = await adminClient.auth.admin.inviteUserByEmail(email, inviteLinkOptions);
            if (error && error.message?.includes("already been registered")) {
              const { data: linkData, error: linkError } = await adminClient.auth.admin.generateLink({
                type: "recovery",
                email,
                ...(recoveryLinkOptions ? { options: recoveryLinkOptions } : {}),
              });
              if (linkError) throw linkError;
              const resetLink = linkData?.properties?.action_link || "";
              if (!resetLink) throw new Error("Could not generate invitation link");
              const sender = await getVerifiedSender(adminClient);
              if (!sender?.id) throw new Error("No verified email sender is configured");
              await sendManagedEmail({
                callerClient,
                senderId: sender.id,
                recipient: email,
                subject: "You've Been Invited",
                html: buildActionEmailHtml({
                  title: "You've Been Invited",
                  intro: "You have been invited to access the system. Click the button below to set up your password and get started:",
                  buttonLabel: "Set Up Password",
                  actionLink: resetLink,
                }),
              });
              results.sent++;
              continue;
            }
            if (error) throw error;

            if (data?.user) {
              const { data: existingProfile } = await adminClient.from("profiles").select("user_id").eq("user_id", data.user.id).maybeSingle();
              if (!existingProfile) {
                await adminClient.from("profiles").insert({ user_id: data.user.id, full_name: "", email });
              }
              const { data: existingRole } = await adminClient.from("user_roles").select("id").eq("user_id", data.user.id).limit(1);
              if (!existingRole?.length) {
                await adminClient.from("user_roles").insert({ user_id: data.user.id, role: "homeowner" });
              }
            }
            results.sent++;
          } catch (e: any) {
            results.errors.push(`${email}: ${e.message}`);
          }
        }

        result = results;
        break;
      }

      case "bulk_onboard_owners": {
        if (!(await checkPermission("create"))) throw new Error("Create permission denied");
        const { owner_ids } = body;
        if (!owner_ids?.length) throw new Error("owner_ids required");

        const { data: owners, error: ownersError } = await adminClient
          .from("owners")
          .select("id, first_name, last_name, email, user_id")
          .in("id", owner_ids);
        if (ownersError) throw ownersError;

        const results: { sent: number; linked: number; skipped: number; errors: string[] } = { sent: 0, linked: 0, skipped: 0, errors: [] };
        const sender = await getVerifiedSender(adminClient).catch(() => null);

        for (const owner of owners || []) {
          const email = String(owner.email || "").trim();
          const fullName = `${owner.first_name || ""} ${owner.last_name || ""}`.trim();
          if (!email) {
            results.skipped++;
            results.errors.push(`${fullName || owner.id}: missing email`);
            continue;
          }

          try {
            let userId = owner.user_id;
            let shouldSendSetupEmail = Boolean(userId);
            if (userId) {
              const { data: linkedUser, error: linkedUserError } = await adminClient.auth.admin.getUserById(userId);
              const linkedEmail = String(linkedUser?.user?.email || "").trim().toLowerCase();
              if (linkedUserError || !linkedUser?.user || linkedEmail !== email.toLowerCase()) {
                userId = null;
                shouldSendSetupEmail = false;
              }
            }

            if (!userId) {
              const { data, error } = await adminClient.auth.admin.inviteUserByEmail(email, inviteLinkOptions);
              if (error && error.message?.includes("already been registered")) {
                const { data: usersData } = await adminClient.auth.admin.listUsers({ page: 1, perPage: 1000 });
                userId = usersData.users.find((u: any) => String(u.email || "").toLowerCase() === email.toLowerCase())?.id || null;
                shouldSendSetupEmail = Boolean(userId);
              } else if (error) {
                throw error;
              } else {
                userId = data?.user?.id || null;
                results.sent++;
              }
            }

            if (!userId) throw new Error("Could not find or create user");

            await adminClient.from("owners").update({ user_id: userId }).eq("id", owner.id);
            results.linked++;

            const { data: existingProfile } = await adminClient.from("profiles").select("user_id").eq("user_id", userId).maybeSingle();
            if (!existingProfile) await adminClient.from("profiles").insert({ user_id: userId, full_name: fullName, email });
            else await adminClient.from("profiles").update({ full_name: fullName, email }).eq("user_id", userId);

            const { data: existingRole } = await adminClient.from("user_roles").select("id").eq("user_id", userId).eq("role", "homeowner").limit(1);
            if (!existingRole?.length) await adminClient.from("user_roles").insert({ user_id: userId, role: "homeowner" });

            if (shouldSendSetupEmail) {
              if (!sender?.id) throw new Error("No verified email sender is configured");
              const { data: linkData, error: linkError } = await adminClient.auth.admin.generateLink({ type: "recovery", email, ...(recoveryLinkOptions ? { options: recoveryLinkOptions } : {}) });
              if (linkError) throw linkError;
              const resetLink = linkData?.properties?.action_link || "";
              if (!resetLink) throw new Error("Could not generate setup link");
              await sendManagedEmail({
                callerClient,
                senderId: sender.id,
                recipient: email,
                subject: "Set Up Your Homeowner Portal Password",
                html: buildActionEmailHtml({ title: "Homeowner Portal Access", intro: "Click the button below to set or update your password for the homeowner portal:", buttonLabel: "Set Password", actionLink: resetLink }),
              });
              results.sent++;
            }
          } catch (e: any) {
            results.errors.push(`${email}: ${e.message}`);
          }
        }

        result = results;
        break;
      }

      case "sync_auth_users": {
        if (!(await checkPermission("create"))) throw new Error("Create permission denied");

        const allAuthUsers = await listAllAuthUsers(adminClient);

        // Get existing profile user_ids
        const { data: existingProfiles } = await adminClient
          .from("profiles")
          .select("user_id");
        const existingIds = new Set((existingProfiles || []).map((p: any) => p.user_id));

        // Find auth users without profiles
        const missing = allAuthUsers.filter((u: any) => !existingIds.has(u.id));

        let synced = 0;
        for (const u of missing) {
          const fullName = u.user_metadata?.full_name || u.email?.split("@")[0] || "";
          
          const { error: profileError } = await adminClient.from("profiles").insert({
            user_id: u.id,
            full_name: fullName,
            email: u.email || "",
          });
          throwIfDbError(profileError, `Could not create profile for ${u.email || u.id}`);

          // Create default role if none exists
          const { data: existingRoles } = await adminClient
            .from("user_roles")
            .select("id")
            .eq("user_id", u.id)
            .limit(1);
          if (!existingRoles?.length) {
            const { error: roleError } = await adminClient.from("user_roles").insert({
              user_id: u.id,
              role: "homeowner",
            });
            throwIfDbError(roleError, `Could not create role for ${u.email || u.id}`);
          }
          synced++;
        }

        result = { success: true, synced, total_auth: allAuthUsers.length, already_existed: existingIds.size };
        break;
      }

      default:
        throw new Error(`Unknown action: ${action}`);
    }

    return new Response(JSON.stringify(result), {
      headers: { ...corsHeaders, "Content-Type": "application/json" },
    });
  } catch (error: any) {
    console.error("Admin user management error:", error);
    return new Response(
      JSON.stringify({ error: error.message || "Internal server error" }),
      {
        status: 400,
        headers: { ...corsHeaders, "Content-Type": "application/json" },
      }
    );
  }
});
