// Buildium Import Stage — fetches data from Buildium and writes a review batch
// into public.buildium_import_staging instead of touching live tables.
import { createClient } from "https://esm.sh/@supabase/supabase-js@2.49.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",
};

const BUILDIUM_BASE = "https://api.buildium.com";
const wait = (ms: number) => new Promise((r) => setTimeout(r, ms));

async function buildiumFetch(path: string, clientId: string, clientSecret: string, params?: Record<string, string>) {
  const url = new URL(`${BUILDIUM_BASE}${path}`);
  if (params) Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v));
  for (let attempt = 0; attempt < 4; attempt++) {
    const res = await fetch(url.toString(), {
      headers: { "x-buildium-client-id": clientId, "x-buildium-client-secret": clientSecret, Accept: "application/json" },
    });
    if (res.ok) return res.json();
    const text = await res.text();
    if ((res.status === 429 || res.status >= 500) && attempt < 3) {
      const ra = Number(res.headers.get("Retry-After") ?? "");
      await wait(Number.isFinite(ra) && ra > 0 ? ra * 1000 : 600 * Math.pow(2, attempt));
      continue;
    }
    throw new Error(`Buildium ${path} [${res.status}]: ${text}`);
  }
  throw new Error(`Buildium ${path} failed after retries`);
}

async function buildiumFetchAll(path: string, clientId: string, clientSecret: string, extra?: Record<string, string>) {
  const all: any[] = [];
  let offset = 0;
  const limit = 50;
  while (true) {
    const page = await buildiumFetch(path, clientId, clientSecret, { ...extra, offset: String(offset), limit: String(limit) });
    if (!Array.isArray(page) || page.length === 0) break;
    all.push(...page);
    if (page.length < limit) break;
    offset += limit;
  }
  return all;
}

const norm = (v: unknown) => String(v ?? "").toLowerCase().replace(/[^a-z0-9]+/g, " ").replace(/\s+/g, " ").trim();
const normId = (v: unknown) => {
  if (v === null || v === undefined) return null;
  const s = String(v).trim();
  return s.length > 0 ? s : null;
};

function formatAddress(a: any): string {
  if (!a) return "";
  const parts = [a.AddressLine1, a.AddressLine2, a.AddressLine3, a.City, a.State, a.PostalCode || a.ZipCode].filter(Boolean);
  return parts.join(", ");
}

function mapGLAccountType(type: string | null | undefined): string {
  const t = String(type || "").toLowerCase();
  if (t.includes("asset")) return "asset";
  if (t.includes("liabilit")) return "liability";
  if (t.includes("equity")) return "equity";
  if (t.includes("income") || t.includes("revenue")) return "income";
  if (t.includes("expense")) return "expense";
  return "expense";
}

function diff(existing: any, incoming: Record<string, any>): Record<string, { from: any; to: any }> {
  const out: Record<string, { from: any; to: any }> = {};
  for (const [k, v] of Object.entries(incoming)) {
    if (v === null || v === undefined || v === "") continue;
    const cur = existing?.[k];
    if (cur !== v && !(cur == null && v == null)) {
      out[k] = { from: cur ?? null, to: v };
    }
  }
  return out;
}

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 serviceKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!;
    const token = authHeader.replace("Bearer ", "");

    let userId: string | null = null;
    try {
      const payload = token.split(".")[1];
      const padded = payload.replace(/-/g, "+").replace(/_/g, "/").padEnd(Math.ceil(payload.length / 4) * 4, "=");
      userId = JSON.parse(atob(padded))?.sub ?? null;
    } catch {
      return new Response(JSON.stringify({ error: "Unauthorized" }), { status: 401, headers: { ...corsHeaders, "Content-Type": "application/json" } });
    }

    const auth = createClient(supabaseUrl, anonKey, { global: { headers: { Authorization: authHeader } } });
    const { data: roles } = await auth.from("user_roles").select("role").eq("user_id", userId);
    const isStaff = (roles || []).some((r: any) => r.role === "admin" || r.role === "manager");
    if (!isStaff) {
      return new Response(JSON.stringify({ error: "Unauthorized" }), { status: 401, headers: { ...corsHeaders, "Content-Type": "application/json" } });
    }

    const supabase = createClient(supabaseUrl, serviceKey);
    const body = await req.json().catch(() => ({}));
    const kinds: string[] = Array.isArray(body.kinds) && body.kinds.length > 0 ? body.kinds : ["unit", "owner", "gl_account", "ledger_entry"];
    const includeArcFiles = body.includeArcFiles !== false;
    const selectedAssociationIds: string[] = Array.isArray(body.selectedAssociationIds) ? body.selectedAssociationIds.filter((s: any) => typeof s === "string" && s) : [];
    const dateFrom = typeof body.dateFrom === "string" ? body.dateFrom : null;
    const dateTo = typeof body.dateTo === "string" ? body.dateTo : null;
    const unitFilterId = typeof body.unitId === "string" && body.unitId ? body.unitId : null;

    const clientId = Deno.env.get("BUILDIUM_API_KEY") ?? "";
    const clientSecret = Deno.env.get("BUILDIUM_API_SECRET") ?? "";
    if (!clientId || !clientSecret) {
      return new Response(JSON.stringify({ error: "Buildium API credentials not configured" }), { status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" } });
    }

    let scopedAssocIds = [...selectedAssociationIds];
    if (unitFilterId && scopedAssocIds.length === 0) {
      const { data: u } = await supabase.from("units").select("association_id").eq("id", unitFilterId).maybeSingle();
      if (u?.association_id) scopedAssocIds = [u.association_id];
    }
    const isSelected = (id: string | null) => scopedAssocIds.length === 0 || (!!id && scopedAssocIds.includes(id));

    const { data: assocRows } = await supabase.from("associations").select("id, name");
    const assocByName = new Map<string, any>();
    for (const a of assocRows || []) assocByName.set(norm(a.name), a);

    const buildiumAssocs = await buildiumFetchAll("/v1/associations", clientId, clientSecret);
    const bAssocIdToLocalId = new Map<string, string>();
    for (const ba of buildiumAssocs) {
      const local = assocByName.get(norm(ba.Name));
      if (local) bAssocIdToLocalId.set(String(ba.Id), local.id);
    }

    const batchId = crypto.randomUUID();
    const stageRows: any[] = [];

    function stage(kind: string, action: string, association_id: string | null, external_id: string | null, summary: string, payload: any, match_id: string | null = null, diffObj: any = null) {
      stageRows.push({
        batch_id: batchId,
        association_id,
        kind,
        action,
        external_id,
        match_id,
        summary,
        payload,
        diff: diffObj,
        status: "pending",
        created_by: userId,
      });
    }

    let buildiumUnits: any[] = [];
    if (kinds.includes("unit") || kinds.includes("owner") || kinds.includes("ledger_entry")) {
      buildiumUnits = await buildiumFetchAll("/v1/associations/units", clientId, clientSecret);
    }

    const { data: unitRows } = await supabase
      .from("units")
      .select("id, association_id, unit_number, address, buildium_unit_id");
    const unitByBuildiumId = new Map<string, any>();
    const unitByAssocAddr = new Map<string, any>();
    const unitByAssocNumber = new Map<string, any>();
    for (const u of unitRows || []) {
      if (u.buildium_unit_id) unitByBuildiumId.set(String(u.buildium_unit_id), u);
      if (u.address) unitByAssocAddr.set(`${u.association_id}|${norm(u.address)}`, u);
      if (u.unit_number) unitByAssocNumber.set(`${u.association_id}|${norm(u.unit_number)}`, u);
    }

    if (kinds.includes("unit")) {
      for (const bu of buildiumUnits) {
        const assocLocalId = bAssocIdToLocalId.get(String(bu.AssociationId)) || null;
        if (!assocLocalId || !isSelected(assocLocalId)) continue;
        const buildium_unit_id = String(bu.Id);
        const unit_number = String(bu.UnitNumber || bu.UnitName || "").trim() || null;
        const address = formatAddress(bu.Address) || null;
        const incoming = { association_id: assocLocalId, unit_number, address, buildium_unit_id, status: "active" };

        const matchById = unitByBuildiumId.get(buildium_unit_id);
        const matchByAddr = !matchById && address ? unitByAssocAddr.get(`${assocLocalId}|${norm(address)}`) : null;
        const matchByNum = !matchById && !matchByAddr && unit_number ? unitByAssocNumber.get(`${assocLocalId}|${norm(unit_number)}`) : null;
        const match = matchById || matchByAddr || matchByNum || null;

        if (match) {
          const d = diff(match, incoming);
          if (Object.keys(d).length === 0) continue;
          stage("unit", "update", assocLocalId, buildium_unit_id, `Unit ${unit_number || address || buildium_unit_id}`, incoming, match.id, d);
        } else {
          stage("unit", "create", assocLocalId, buildium_unit_id, `Unit ${unit_number || address || buildium_unit_id}`, incoming, null, null);
        }
      }
    }

    if (kinds.includes("owner")) {
      const owners = await buildiumFetchAll("/v1/associations/owners", clientId, clientSecret);
      const { data: ownerRows } = await supabase
        .from("owners")
        .select("id, association_id, first_name, last_name, property_address, mailing_address, email, phone, buildium_owner_id, unit_id, status");
      const ownerByBuildiumId = new Map<string, any>();
      const ownerByKey = new Map<string, any>();
      for (const o of ownerRows || []) {
        if (o.buildium_owner_id) ownerByBuildiumId.set(String(o.buildium_owner_id), o);
        ownerByKey.set(`${o.association_id}|${norm(o.first_name)}|${norm(o.last_name)}|${norm(o.property_address)}`, o);
      }

      const ownershipAccounts = await buildiumFetchAll("/v1/associations/ownershipaccounts", clientId, clientSecret);
      const acctByOwnerId = new Map<string, any[]>();
      for (const acct of ownershipAccounts) {
        const ids: string[] = [];
        if (Array.isArray(acct.AssociationOwnerIds)) for (const id of acct.AssociationOwnerIds) ids.push(String(id));
        if (acct.AssociationOwnerId) ids.push(String(acct.AssociationOwnerId));
        for (const id of ids) {
          if (!acctByOwnerId.has(id)) acctByOwnerId.set(id, []);
          acctByOwnerId.get(id)!.push(acct);
        }
      }

      for (const o of owners) {
        const buildium_owner_id = String(o.Id);
        const first_name = (o.FirstName || "").trim() || "Unknown";
        const last_name = (o.LastName || "").trim() || "Owner";
        const property_address = formatAddress(o.PrimaryAddress) || "N/A";
        const mailing_address = formatAddress(o.AlternateAddress) || null;
        let assocLocalId = bAssocIdToLocalId.get(String(o.AssociationId)) || null;

        const linked = acctByOwnerId.get(buildium_owner_id) || [];
        if (!assocLocalId) {
          for (const acct of linked) {
            const id = bAssocIdToLocalId.get(String(acct.AssociationId));
            if (id) { assocLocalId = id; break; }
          }
        }
        if (!assocLocalId || !isSelected(assocLocalId)) continue;

        let unitLocalId: string | null = null;
        let unitBuildiumIdForResolve: string | null = null;
        for (const acct of linked) {
          const bUid = normId(acct.UnitId);
          if (bUid) {
            unitBuildiumIdForResolve = bUid;
            const u = unitByBuildiumId.get(bUid);
            if (u) { unitLocalId = u.id; break; }
          }
        }

        const isCurrent = linked.some((a: any) => {
          const s = String(a.Status || "").toLowerCase();
          return s === "" || s === "active" || s === "current";
        });
        const status = isCurrent ? "active" : "archived";

        const incoming = {
          association_id: assocLocalId,
          first_name, last_name,
          property_address, mailing_address,
          email: o.Email || null,
          phone: o.PhoneNumbers?.[0]?.Number || null,
          buildium_owner_id,
          unit_id: unitLocalId,
          status,
          _resolve_unit_buildium_id: unitBuildiumIdForResolve,
        };

        const match = ownerByBuildiumId.get(buildium_owner_id) || ownerByKey.get(`${assocLocalId}|${norm(first_name)}|${norm(last_name)}|${norm(property_address)}`) || null;

        if (match) {
          const d = diff(match, { ...incoming, _resolve_unit_buildium_id: undefined });
          if (Object.keys(d).length === 0) continue;
          stage("owner", "update", assocLocalId, buildium_owner_id, `${first_name} ${last_name}`, incoming, match.id, d);
        } else {
          stage("owner", "create", assocLocalId, buildium_owner_id, `${first_name} ${last_name}${property_address && property_address !== "N/A" ? ` — ${property_address}` : ""}`, incoming, null, null);
        }
      }
    }

    if (kinds.includes("gl_account")) {
      const gls = await buildiumFetchAll("/v1/glaccounts", clientId, clientSecret);
      for (const a of assocRows || []) {
        if (!isSelected(a.id)) continue;
        const { data: existing } = await supabase
          .from("chart_of_accounts")
          .select("id, association_id, account_number, account_name, account_type, description, is_active")
          .eq("association_id", a.id);
        const byNum = new Map<string, any>();
        for (const e of existing || []) byNum.set(String(e.account_number), e);
        for (const gl of gls) {
          const account_number = String(gl.Id || gl.AccountNumber || "");
          if (!account_number) continue;
          const incoming = {
            association_id: a.id,
            account_number,
            account_name: gl.Name || "Unknown",
            account_type: mapGLAccountType(gl.Type || gl.AccountType),
            description: gl.Description || null,
            is_active: gl.IsActive !== false,
            _parent_buildium_id: gl.ParentGLAccountId ? String(gl.ParentGLAccountId) : null,
          };
          const match = byNum.get(account_number);
          if (match) {
            const d = diff(match, { ...incoming, _parent_buildium_id: undefined });
            if (Object.keys(d).length === 0) continue;
            stage("gl_account", "update", a.id, account_number, `${account_number} — ${incoming.account_name} (${a.name})`, incoming, match.id, d);
          } else {
            stage("gl_account", "create", a.id, account_number, `${account_number} — ${incoming.account_name} (${a.name})`, incoming, null, null);
          }
        }
      }
    }

    if (kinds.includes("ledger_entry")) {
      const ownershipAccounts = await buildiumFetchAll("/v1/associations/ownershipaccounts", clientId, clientSecret);

      const { data: ownerAll } = await supabase.from("owners").select("id, buildium_owner_id, unit_id, association_id").not("buildium_owner_id", "is", null);
      const ownerByBuildium = new Map<string, any>();
      for (const o of ownerAll || []) ownerByBuildium.set(String(o.buildium_owner_id), o);

      const existingRefs = new Set<string>();
      const { data: existingLedger } = await supabase
        .from("owner_ledger_entries")
        .select("unit_id, reference_id")
        .eq("reference_type", "buildium")
        .not("reference_id", "is", null)
        .limit(50000);
      for (const r of existingLedger || []) if (r.unit_id && r.reference_id) existingRefs.add(`${r.unit_id}|${r.reference_id}`);

      for (const acct of ownershipAccounts) {
        const assocLocalId = bAssocIdToLocalId.get(String(acct.AssociationId)) || null;
        if (!assocLocalId || !isSelected(assocLocalId)) continue;
        const buildiumUnitId = normId(acct.UnitId);
        if (!buildiumUnitId) continue;
        const unit = unitByBuildiumId.get(buildiumUnitId) || null;
        if (!unit) continue;
        if (unitFilterId && unit.id !== unitFilterId) continue;

        const txnParams: Record<string, string> = {};
        if (dateFrom) txnParams.transactiondatefrom = dateFrom;
        if (dateTo) txnParams.transactiondateto = dateTo;
        let entries: any[] = [];
        try {
          entries = await buildiumFetchAll(`/v1/associations/ownershipaccounts/${acct.Id}/transactions`, clientId, clientSecret, Object.keys(txnParams).length ? txnParams : undefined);
        } catch (e) {
          console.warn(`Ledger fetch ${acct.Id}: ${e}`);
          continue;
        }

        for (const e of entries) {
          const refId = String(e.Id || "");
          if (!refId) continue;
          const amount = Number(e.TotalAmount ?? e.Amount ?? 0);
          if (amount === 0) continue;
          const txType = String(e.TransactionType || "");
          const txDate = e.Date ? String(e.Date).split("T")[0] : new Date().toISOString().split("T")[0];

          if (/prepayment/i.test(txType) || /prepayment/i.test(String(e.Description || e.Memo || ""))) {
            if (!/sirs reserve prepayment/i.test(String(e.Description || e.Memo || ""))) continue;
          }

          const isPayment = amount < 0 || /payment|credit|deposit|refund/i.test(txType);
          // POLICY: Only PULL payments from Buildium. Charges originate locally.
          if (!isPayment) continue;
          const debit = isPayment ? 0 : Math.abs(amount);
          const credit = isPayment ? Math.abs(amount) : 0;
          const desc = String(e.Description || e.Memo || e.Journal?.Memo || txType || "Buildium entry").slice(0, 500);

          if (existingRefs.has(`${unit.id}|${refId}`)) continue;

          let ownerLocalId: string | null = null;
          const buildiumOwnerIds: string[] = [];
          if (Array.isArray(acct.AssociationOwnerIds)) for (const id of acct.AssociationOwnerIds) buildiumOwnerIds.push(String(id));
          if (acct.AssociationOwnerId) buildiumOwnerIds.push(String(acct.AssociationOwnerId));
          for (const boid of buildiumOwnerIds) {
            const o = ownerByBuildium.get(boid);
            if (o) { ownerLocalId = o.id; break; }
          }

          const incoming = {
            association_id: assocLocalId,
            unit_id: unit.id,
            owner_id: ownerLocalId,
            date: txDate,
            transaction_type: txType,
            description: desc,
            debit, credit,
            reference_id: refId,
            reference_type: "buildium",
            _resolve_unit_buildium_id: buildiumUnitId,
            _resolve_owner_buildium_ids: buildiumOwnerIds,
          };

          stage(
            "ledger_entry",
            "create",
            assocLocalId,
            refId,
            `${txDate} • ${desc.slice(0, 80)} • ${debit > 0 ? `-$${debit.toFixed(2)}` : `+$${credit.toFixed(2)}`}`,
            incoming,
            null,
            null,
          );
        }
      }
    }

    // ---- ARC APPLICATIONS ----
    if (kinds.includes("arc_application")) {
      // Map Buildium Status + Decision to local status.
      // Buildium fields: Status = New | InProgress | Closed; Decision = Pending | Approved | Denied
      function mapArcStatus(status: string | null | undefined, decision: string | null | undefined): string {
        const d = String(decision || "").toLowerCase();
        if (d.includes("approve")) return "approved";
        if (d.includes("den") || d.includes("reject")) return "denied";
        const s = String(status || "").toLowerCase();
        if (s.includes("withdraw") || s.includes("cancel")) return "withdrawn";
        if (s.includes("progress") || s.includes("review")) return "under_review";
        if (s.includes("closed")) return "approved";
        return "submitted";
      }

      // Existing ARC apps for matching by buildium id
      const { data: existingArc } = await supabase
        .from("arc_applications")
        .select("id, association_id, buildium_arc_request_id, title, description, status, decision_notes, review_date, submitted_date, unit_id, owner_id");
      const arcByBuildium = new Map<string, any>();
      for (const a of existingArc || []) {
        if (a.buildium_arc_request_id) arcByBuildium.set(`${a.association_id}|${a.buildium_arc_request_id}`, a);
      }

      // Owner lookup by buildium id
      const { data: ownersAll } = await supabase
        .from("owners")
        .select("id, unit_id, association_id, buildium_owner_id")
        .not("buildium_owner_id", "is", null);
      const ownerByBuildiumLocal = new Map<string, any>();
      for (const o of ownersAll || []) ownerByBuildiumLocal.set(String(o.buildium_owner_id), o);

      for (const ba of buildiumAssocs) {
        const assocLocalId = bAssocIdToLocalId.get(String(ba.Id));
        if (!assocLocalId || !isSelected(assocLocalId)) continue;

        let arcRequests: any[] = [];
        try {
          arcRequests = await buildiumFetchAll(
            `/v1/associations/ownershipaccounts/architecturalrequests`,
            clientId,
            clientSecret,
            { associationids: String(ba.Id) },
          );
        } catch (e) {
          console.warn(`ARC fetch for association ${ba.Id} failed: ${e}`);
          continue;
        }

        for (const r of arcRequests) {
          const buildiumArcId = String(r.Id);
          // Buildium ARC payload doesn't expose UnitId — resolve unit via the owner's unit later.
          const buildiumUnitId = normId(r.UnitId);
          const buildiumOwnerId = normId(r.OwnershipAccountId || r.AssociationOwnerId);
          const localOwner = buildiumOwnerId ? ownerByBuildiumLocal.get(buildiumOwnerId) : null;
          const localUnit = buildiumUnitId
            ? unitByBuildiumId.get(buildiumUnitId)
            : (localOwner?.unit_id ? { id: localOwner.unit_id } : null);

          // Buildium uses `Name` for the request title and does not expose a description on this resource.
          const rawName = String(r.Name || r.Title || r.Subject || "").trim();
          const title = rawName || `Architectural Request #${buildiumArcId}`;
          const description = String(r.Description || "").trim();

          const submittedDate = r.SubmittedDateTime
            ? String(r.SubmittedDateTime).split("T")[0]
            : (r.SubmittedDate ? String(r.SubmittedDate).split("T")[0] : null);
          const decisionDate = r.DecisionDateTime
            ? String(r.DecisionDateTime).split("T")[0]
            : (r.DecisionDate ? String(r.DecisionDate).split("T")[0] : null);
          const decisionNotes = r.DecisionDescription
            ? String(r.DecisionDescription)
            : (r.Decision && !/pending/i.test(String(r.Decision)) ? `Decision: ${r.Decision}` : null);
          const status = mapArcStatus(r.Status, r.Decision);

          // Buildium exposes the user who last touched the request — use as best-available "decider"
          const decider = r.LastUpdatedByUser || r.UpdatedByUser || null;
          const deciderName = decider
            ? [decider.FirstName, decider.LastName].filter(Boolean).join(" ").trim() || null
            : null;
          const deciderDate = (r.LastUpdatedDateTime || decider?.UpdatedDateTime || decisionDate || "")
            ?.toString()
            .split("T")[0] || null;

          // Optional file pull
          let files: Array<{ id: string; name: string; download_url: string | null }> = [];
          if (includeArcFiles) {
            try {
              const fileList = await buildiumFetchAll(
                `/v1/associations/ownershipaccounts/architecturalrequests/${buildiumArcId}/files`,
                clientId,
                clientSecret,
              );
              files = (fileList || []).map((f: any) => ({
                id: String(f.Id || f.FileId || ""),
                name: String(f.Title || f.FileName || f.Name || f.PhysicalFileName || `file-${f.Id}`),
                download_url: null,
              }));
            } catch (e) {
              console.warn(`ARC files fetch failed for ${buildiumArcId}: ${e}`);
            }
          }

          const incoming: Record<string, any> = {
            association_id: assocLocalId,
            unit_id: localUnit?.id || null,
            owner_id: localOwner?.id || null,
            title,
            description: description || null,
            project_type: r.ProjectType || r.Type || null,
            status,
            submitted_date: submittedDate,
            review_date: decisionDate,
            decision_notes: decisionNotes,
            buildium_arc_request_id: buildiumArcId,
            _resolve_unit_buildium_id: buildiumUnitId,
            _resolve_owner_buildium_id: buildiumOwnerId,
            _arc_files: files,
            _arc_buildium_association_id: String(ba.Id),
            _arc_decider_name: deciderName,
            _arc_decider_date: deciderDate,
          };

          const match = arcByBuildium.get(`${assocLocalId}|${buildiumArcId}`);
          if (match) {
            const d = diff(match, {
              status: incoming.status,
              decision_notes: incoming.decision_notes,
              review_date: incoming.review_date,
              title: incoming.title,
              description: incoming.description,
            });
            if (Object.keys(d).length === 0 && (!includeArcFiles || files.length === 0)) continue;
            stage(
              "arc_application",
              "update",
              assocLocalId,
              buildiumArcId,
              `${title} — ${status}${files.length ? ` · ${files.length} file${files.length > 1 ? "s" : ""}` : ""}`,
              incoming,
              match.id,
              d,
            );
          } else {
            stage(
              "arc_application",
              "create",
              assocLocalId,
              buildiumArcId,
              `${title} — ${status}${files.length ? ` · ${files.length} file${files.length > 1 ? "s" : ""}` : ""}`,
              incoming,
              null,
              null,
            );
          }
        }
      }
    }

    let inserted = 0;
    for (let i = 0; i < stageRows.length; i += 500) {
      const slice = stageRows.slice(i, i + 500);
      const { error } = await supabase.from("buildium_import_staging").insert(slice);
      if (error) throw error;
      inserted += slice.length;
    }

    const counts: Record<string, number> = {};
    for (const r of stageRows) counts[r.kind] = (counts[r.kind] || 0) + 1;

    return new Response(JSON.stringify({ success: true, batch_id: batchId, inserted, counts }), {
      status: 200,
      headers: { ...corsHeaders, "Content-Type": "application/json" },
    });
  } catch (e: any) {
    console.error("buildium-import-stage error", e);
    return new Response(JSON.stringify({ error: e?.message || String(e) }), { status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" } });
  }
});
