// (POST handler is defined later in this file)
export const dynamic = "force-dynamic";
import { NextResponse, NextRequest } from "next/server";
import { createSbServer, createSbServiceRole } from "@/lib/supabaseServer";
import { resolveActiveOrg } from "@/lib/resolveActiveOrg";
import { sendUniversalEmailV2 } from "@/lib/emailTemplateService";
import { generateAutoDeclarationToken } from "@/lib/autoDeclarationTokenService";

type SalarieRow = {
  matricule: string;
  nom: string;
  email?: string | null;
  transat_connecte?: boolean;
  dernier_emploi?: string | null;
  code_salarie?: string | null;
  prenom?: string | null;
  civilite?: string | null;
  tel?: string | null;
  org_id?: string | null;
  org_name?: string | null;
};

type SalariesResponse = {
  items: SalarieRow[];
  page: number;
  limit: number;
  total?: number | null;
  totalPages?: number;
  hasMore: boolean;
};

async function resolveActiveOrgId(sb: any, _isStaff: boolean) {
  return await resolveActiveOrg(sb);
}

export async function GET(req: NextRequest) {
  try {
    // 🎭 Mode démo : retourner des données fictives
    const isDemoMode = process.env.DEMO_MODE === 'true';
    
    if (isDemoMode) {
      console.log('🎭 [API /salaries] Mode démo - retour de données fictives');
      
      const { searchParams } = new URL(req.url);
      const page = Math.max(1, parseInt(searchParams.get("page") || "1", 10) || 1);
      const limit = Math.min(100, Math.max(1, parseInt(searchParams.get("limit") || "10", 10) || 10));
      const q = (searchParams.get("search") || searchParams.get("q") || "").trim();
      
      // Données fictives de salariés
      const DEMO_SALARIES: SalarieRow[] = [
        {
          matricule: "demo-sal-001",
          nom: "MARTIN Alice",
          email: "alice.martin@demo.fr",
          transat_connecte: true,
          dernier_emploi: "Comédien",
          code_salarie: "demo-sal-001",
          prenom: "Alice",
          civilite: "Mme",
          tel: "06 12 34 56 78",
          org_id: "demo-org",
          org_name: "Organisation Démo",
        },
        {
          matricule: "demo-sal-002",
          nom: "DUBOIS Pierre",
          email: "pierre.dubois@demo.fr",
          transat_connecte: false,
          dernier_emploi: "Metteur en scène",
          code_salarie: "demo-sal-002",
          prenom: "Pierre",
          civilite: "M.",
          tel: "06 23 45 67 89",
          org_id: "demo-org",
          org_name: "Organisation Démo",
        },
        {
          matricule: "demo-sal-003",
          nom: "LEROY Sophie",
          email: "sophie.leroy@demo.fr",
          transat_connecte: true,
          dernier_emploi: "Danseur",
          code_salarie: "demo-sal-003",
          prenom: "Sophie",
          civilite: "Mme",
          tel: "06 34 56 78 90",
          org_id: "demo-org",
          org_name: "Organisation Démo",
        },
        {
          matricule: "demo-sal-004",
          nom: "BERNARD Marc",
          email: "marc.bernard@demo.fr",
          transat_connecte: false,
          dernier_emploi: "Technicien son",
          code_salarie: "demo-sal-004",
          prenom: "Marc",
          civilite: "M.",
          tel: "06 45 67 89 01",
          org_id: "demo-org",
          org_name: "Organisation Démo",
        },
        {
          matricule: "demo-sal-005",
          nom: "GARCIA Elena",
          email: "elena.garcia@demo.fr",
          transat_connecte: true,
          dernier_emploi: "Costumière",
          code_salarie: "demo-sal-005",
          prenom: "Elena",
          civilite: "Mme",
          tel: "06 56 78 90 12",
          org_id: "demo-org",
          org_name: "Organisation Démo",
        },
      ];
      
      // Filtrer par recherche si nécessaire
      let filteredSalaries = DEMO_SALARIES;
      if (q) {
        const searchTerm = q.toLowerCase();
        filteredSalaries = DEMO_SALARIES.filter(salarie =>
          salarie.nom.toLowerCase().includes(searchTerm) ||
          salarie.prenom?.toLowerCase().includes(searchTerm) ||
          salarie.matricule.toLowerCase().includes(searchTerm) ||
          salarie.email?.toLowerCase().includes(searchTerm) ||
          salarie.dernier_emploi?.toLowerCase().includes(searchTerm)
        );
      }
      
      // Pagination
      const offset = (page - 1) * limit;
      const paginatedSalaries = filteredSalaries.slice(offset, offset + limit);
      const total = filteredSalaries.length;
      const hasMore = offset + paginatedSalaries.length < total;
      const totalPages = Math.ceil(total / limit);
      
      const payload: SalariesResponse = {
        items: paginatedSalaries,
        page,
        limit,
        total,
        totalPages,
        hasMore,
      };
      
      return NextResponse.json(payload);
    }
    
    const sb = createSbServer();

    const {
      data: { user },
      error: authError,
    } = await sb.auth.getUser();
    if (authError || !user) {
      return NextResponse.json({ error: "Non authentifié" }, { status: 401 });
    }

    // Determine if current user is staff
    let isStaff = false;
    try {
      const { data } = await sb
        .from("staff_users")
        .select("is_staff")
        .eq("user_id", user.id)
        .maybeSingle();
      isStaff = !!data?.is_staff;
    } catch {}

    const orgId = await resolveActiveOrgId(sb, isStaff);

  const { searchParams } = new URL(req.url);
  const page = Math.max(1, parseInt(searchParams.get("page") || "1", 10) || 1);
  const limit = Math.min(100, Math.max(1, parseInt(searchParams.get("limit") || "25", 10) || 25));
  // Accept either `search` or `q` (some clients send `q`)
  const q = (searchParams.get("search") || searchParams.get("q") || "").trim();

  console.log("👥 API salaries search:", { q, page, limit, user_id: user.id });

    const offset = (page - 1) * limit;
    const to = offset + limit - 1;

    // Build query
    let query = sb
      .from("salaries")
      .select(
        `id, code_salarie, num_salarie, salarie, nom, prenom, civilite, adresse_mail, compte_transat, derniere_profession, employer_id, infos_caisses_organismes, tel`,
        { count: "exact" }
      );

    // If an organization id was resolved, scope results to that employer.
    if (orgId) {
      query = query.eq("employer_id", orgId);
    }

    if (q) {
      const like = `%${q}%`;
      // Use OR with ilike across relevant columns
      query = query.or(
        [
          `nom.ilike.${like}`,
          `prenom.ilike.${like}`,
          `salarie.ilike.${like}`,
          `adresse_mail.ilike.${like}`,
          `code_salarie.ilike.${like}`,
          `num_salarie.ilike.${like}`,
        ].join(",")
      );
    }

    // Order by last name then first name for stability
    query = query.order("nom", { ascending: true }).order("prenom", { ascending: true, nullsFirst: true }).range(offset, to);

    const { data, error, count } = await query;
    console.log("👥 API salaries result:", { 
      data: data?.length, 
      error: error?.message,
      count
    });
    if (error) {
      console.error("💥 [API /salaries] Supabase error:", error.message);
      return NextResponse.json({ error: "supabase_error", detail: error.message }, { status: 500 });
    }

    const items: SalarieRow[] = (data || []).map((r: any) => {
      const fullName = r.salarie || [r.nom, r.prenom].filter(Boolean).join(" ").trim() || r.nom || "";
      const comp = (r.compte_transat || "").toString().toLowerCase();
      const transat = comp.includes("connect") && !comp.includes("non");
      return {
        matricule: r.code_salarie || (r.num_salarie ? String(r.num_salarie) : r.id),
        nom: fullName,
        email: r.adresse_mail ?? null,
        transat_connecte: transat,
        dernier_emploi: r.derniere_profession ?? null,
        code_salarie: r.code_salarie ?? null,
        prenom: r.prenom ?? null,
        civilite: r.civilite ?? null,
        tel: r.tel ?? null,
        org_id: r.employer_id || null,
        org_name: r.infos_caisses_organismes || null,
      } as SalarieRow;
    });

    const total = count ?? null;
    const hasMore = typeof total === "number" ? offset + items.length < total : items.length === limit;
    const totalPages = typeof total === "number" ? Math.ceil(total / limit) : 0;

    const payload: SalariesResponse = {
      items,
      page,
      limit,
      total,
      totalPages,
      hasMore,
    };

    return NextResponse.json(payload);
  } catch (e: any) {
    console.error("💥 [API /salaries] Unexpected error:", e?.message);
    return NextResponse.json({ error: "server_error", message: e?.message || "unknown" }, { status: 500 });
  }
}

// Rate limiting map: userId -> { count, windowStart }
const rateLimitMap = new Map<string, { count: number; windowStart: number }>();
const RATE_LIMIT_MAX = 50; // 50 salariés par heure
const RATE_LIMIT_WINDOW = 60 * 60 * 1000; // 1 heure en millisecondes

// POST: create a new salary record (used by the nouveau salarié form)
export async function POST(req: NextRequest) {
  try {
    let body: any = {};
    try {
      body = await req.json();
    } catch {
      return NextResponse.json({ ok: false, error: 'invalid_json' }, { status: 400 });
    }

    if (!body || !body.nom || !body.prenom || !body.email_salarie) {
      return NextResponse.json({ ok: false, error: 'missing_required_fields' }, { status: 400 });
    }

    // 🔒 SÉCURITÉ : Validation du format email salarié
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (!emailRegex.test(body.email_salarie)) {
      console.error('❌ [SÉCURITÉ] Format d\'email salarié invalide:', body.email_salarie);
      return NextResponse.json(
        { 
          ok: false, 
          error: 'invalid_email',
          message: 'Le format de l\'email du salarié est invalide'
        },
        { status: 400 }
      );
    }

    const supabase = createSbServiceRole();

    // 🔒 SÉCURITÉ : Authentification et récupération de l'utilisateur
    const sbAuth = createSbServer();
    const { data: { user }, error: authErr } = await sbAuth.auth.getUser();
    
    if (authErr || !user) {
      console.error('❌ [SÉCURITÉ] Utilisateur non authentifié');
      return NextResponse.json(
        { ok: false, error: 'unauthorized', message: 'Authentification requise' },
        { status: 401 }
      );
    }

    // 🔒 SÉCURITÉ : Vérifier si l'utilisateur est staff
    let isStaff = false;
    try {
      const { data: staffRow } = await sbAuth.from('staff_users').select('is_staff').eq('user_id', user.id).maybeSingle();
      isStaff = !!staffRow?.is_staff;
    } catch {
      const userMeta = user?.user_metadata || {};
      const appMeta = user?.app_metadata || {};
      isStaff = Boolean((userMeta.is_staff === true || userMeta.role === 'staff') || (Array.isArray(appMeta.roles) && appMeta.roles.includes('staff')));
    }
    
    console.log('🔍 [SÉCURITÉ] Vérification utilisateur:', { 
      email: user.email, 
      isStaff, 
      employer_id_fourni: body.employer_id 
    });

    // 🔒 SÉCURITÉ : Rate limiting (50 créations par heure par utilisateur)
    const now = Date.now();
    const userRateLimit = rateLimitMap.get(user.id);
    
    if (userRateLimit) {
      // Vérifier si on est dans la même fenêtre temporelle
      if (now - userRateLimit.windowStart < RATE_LIMIT_WINDOW) {
        if (userRateLimit.count >= RATE_LIMIT_MAX) {
          const remainingTime = Math.ceil((RATE_LIMIT_WINDOW - (now - userRateLimit.windowStart)) / 60000);
          console.warn('⚠️ [RATE LIMIT] Limite atteinte pour utilisateur:', user.id);
          return NextResponse.json(
            { 
              ok: false, 
              error: 'rate_limit_exceeded',
              message: `Limite de ${RATE_LIMIT_MAX} créations par heure atteinte. Réessayez dans ${remainingTime} minutes.`
            },
            { status: 429 }
          );
        }
        // Incrémenter le compteur
        userRateLimit.count++;
      } else {
        // Nouvelle fenêtre temporelle
        rateLimitMap.set(user.id, { count: 1, windowStart: now });
      }
    } else {
      // Première création pour cet utilisateur
      rateLimitMap.set(user.id, { count: 1, windowStart: now });
    }

    // Nettoyer les entrées expirées du rate limit map (éviter la fuite mémoire)
    for (const [userId, data] of rateLimitMap.entries()) {
      if (now - data.windowStart > RATE_LIMIT_WINDOW) {
        rateLimitMap.delete(userId);
      }
    }

    // 🔒 SÉCURITÉ : Logique différente pour staff et clients
    let orgId: string | null = null;
    
    if (isStaff) {
      // Pour les utilisateurs staff : accepter l'employer_id fourni
      orgId = body.employer_id || null;
      
      if (!orgId) {
        console.error('❌ [STAFF] employer_id manquant dans la requête');
        return NextResponse.json(
          { 
            ok: false, 
            error: 'no_organization',
            message: 'Vous devez sélectionner une organisation'
          },
          { status: 400 }
        );
      }
      
      // Vérifier que l'organisation existe
      const { data: orgExists, error: orgError } = await sbAuth
        .from('organizations')
        .select('id')
        .eq('id', orgId)
        .maybeSingle();
      
      if (orgError || !orgExists) {
        console.error('❌ [STAFF] Organisation introuvable:', orgId);
        return NextResponse.json(
          { 
            ok: false, 
            error: 'invalid_organization',
            message: 'Organisation introuvable'
          },
          { status: 404 }
        );
      }
      
      console.log('✅ [STAFF] Organisation validée:', orgId);
    } else {
      // Pour les utilisateurs clients : utiliser leur organisation associée
      const userOrgId = await resolveActiveOrg(sbAuth);
      
      if (!userOrgId) {
        console.error('❌ [CLIENT] Aucune organisation trouvée pour l\'utilisateur:', user.id);
        return NextResponse.json(
          { 
            ok: false, 
            error: 'no_organization',
            message: 'Aucune organisation associée à votre compte'
          },
          { status: 403 }
        );
      }

      // 🔒 SÉCURITÉ CRITIQUE : Vérifier que l'employer_id fourni (si présent) correspond à l'organisation de l'utilisateur
      if (body.employer_id && body.employer_id !== userOrgId) {
        console.error('❌ [SÉCURITÉ CRITIQUE] Tentative de création salarié dans une autre organisation!');
        console.error('   - employer_id fourni:', body.employer_id);
        console.error('   - organisation utilisateur:', userOrgId);
        console.error('   - utilisateur:', user.email);
        
        return NextResponse.json(
          { 
            ok: false, 
            error: 'unauthorized_organization',
            message: 'Vous ne pouvez pas créer un salarié dans cette organisation'
          },
          { status: 403 }
        );
      }
      
      // Utiliser l'organisation de l'utilisateur authentifié
      orgId = userOrgId;
      
      console.log('✅ [CLIENT] Organisation validée:', orgId);
    }
    
    console.log('✅ [SÉCURITÉ] Vérifications réussies');
    console.log('   - Utilisateur:', user.email);
    console.log('   - Type:', isStaff ? 'STAFF' : 'CLIENT');
    console.log('   - Organisation:', orgId);
    console.log('   - Email salarié validé:', body.email_salarie);
    console.log('   - Rate limit:', `${rateLimitMap.get(user.id)?.count || 0}/${RATE_LIMIT_MAX}`)

    // Compute next matricule/code_salarie for this org if possible
    let computedCode: string | null = null;
    let computedNum: number | null = null;
    if (orgId) {
      try {
        // Récupérer le code employeur depuis organization_details
        const { data: orgDetailsData, error: orgDetailsError } = await supabase
          .from('organization_details')
          .select('code_employeur')
          .eq('org_id', orgId)
          .single();
        
        console.log('🔍 [MATRICULE] Requête organization_details:', { 
          orgId, 
          orgDetailsData, 
          orgDetailsError,
          hasCodeEmployeur: !!orgDetailsData?.code_employeur 
        });
        
        const codeEmployeur = orgDetailsData?.code_employeur || '';
        console.log('🔍 [MATRICULE] code_employeur final:', codeEmployeur);

        const { data: rows, error: qerr } = await supabase
          .from('salaries')
          .select('code_salarie')
          .eq('employer_id', orgId)
          .not('code_salarie', 'is', null)
          .limit(1000);

        if (!qerr && Array.isArray(rows)) {
          console.log('🔍 [MATRICULE] Nombre de salariés existants:', rows.length);
          let maxNum = -Infinity;
          let detectedPrefix = '';
          let maxPad = 0;
          
          for (const r of rows) {
            const s = (r?.code_salarie || '').toString();
            const m = s.match(/(\d+)$/);
            if (m) {
              const numStr = m[1];
              const num = parseInt(numStr, 10);
              if (!Number.isNaN(num) && num > maxNum) {
                maxNum = num;
                detectedPrefix = s.slice(0, s.length - numStr.length);
                maxPad = numStr.length;
              }
            }
          }
          
          if (maxNum !== -Infinity) {
            // On a trouvé des matricules existants
            const nextNum = maxNum + 1;
            const padded = String(nextNum).padStart(Math.max(maxPad, 3), '0');
            
            // Utiliser le code employeur comme préfixe, même si les anciens n'en avaient pas
            const prefix = codeEmployeur || detectedPrefix;
            computedCode = `${prefix}${padded}`;
            computedNum = nextNum;
            console.log('✅ [MATRICULE] Incrémentation:', computedCode, '(prefix utilisé:', prefix, 'prefix détecté:', detectedPrefix, ')');
          } else {
            // No existing numeric matricules found: use code_employeur as prefix
            computedNum = 1;
            computedCode = codeEmployeur ? `${codeEmployeur}001` : `001`;
            console.log('✅ [MATRICULE] Premier salarié, code généré:', computedCode, '(codeEmployeur:', codeEmployeur, ')');
          }
        }
      } catch (e) {
        console.error('❌ [MATRICULE] Erreur génération:', e);
        // ignore computation errors and continue without code
      }
    }

    const fullName = [body.nom, body.prenom].filter(Boolean).join(" ").trim();

    // Only include columns that exist in the `salaries` table to avoid PGRST204
    const payload: any = {
      salarie: fullName || null,
      nom: body.nom || null,
      prenom: body.prenom || null,
      nom_de_naissance: body.nomnaissance_salarie || body.nom_naissance || null,
      // pseudonyme fallback to 'n/a' when empty
      pseudonyme: (body.pseudonyme || body.pseudo || "").toString().trim() || "n/a",
      civilite: body.civilite_salarie || body.civilite || null,
      // map address text into the `adresse` column (table has `adresse`)
      adresse: body.adresse_salarie || body.adresse || null,
      // keep primary contact fields that exist in the table
      adresse_mail: body.email_salarie || body.email || null,
      tel: body.tel_salarie || body.telephone || null,
      date_naissance: body.dob_salarie || body.dateNaissance || null,
      lieu_de_naissance: body.cob_salarie || body.lieuNaissance || null,
      nir: body.ss_salarie || body.nir || null,
      conges_spectacles: body.cs_salarie || body.conges_spectacles || null,
      iban: body.iban_salarie || body.iban || null,
      bic: body.bic_salarie || body.bic || null,
      // enforce requested defaults
      compte_transat: "En cours",
      topaze: "NC",
      justificatifs_personnels: body.justificatifs_personnels || "En attente de réception",
      rf_au_sens_fiscal: true,
      intermittent_mineur_16: false,
      abattement_2024: body.abattement_2024 || "Sans objet",
      notif_nouveau_salarie: "Fait",
      notif_employeur: "Fait",
      infos_caisses_organismes: body.structure || null,
      employer_id: orgId || body.employer_id || null,
      // computed matricule fields
      code_salarie: computedCode,
      num_salarie: computedNum,
    };

    const { data, error } = await supabase.from('salaries').insert([payload]).select().single();
    if (error) {
      console.error('❌ [API /salaries POST] supabase error', error);
      return NextResponse.json({ ok: false, error: 'db_error', detail: error }, { status: 500 });
    }

    // Envoyer les e-mails de notification après la création réussie
    try {
      const sbAuth = createSbServer();
      const { data: { user } } = await sbAuth.auth.getUser();

      // Récupérer les infos de l'organisation depuis les deux tables
      const orgData = orgId ? await supabase.from('organizations').select('name').eq('id', orgId).single() : { data: null };
      const orgDetailsResult = orgId ? await supabase.from('organization_details').select('code_employeur, prenom_contact, email_notifs, email_notifs_cc').eq('org_id', orgId).single() : { data: null, error: null };
      
      console.log('🔍 [EMAIL] Données récupérées:', {
        orgId,
        orgData: orgData?.data,
        orgDetailsData: 'data' in orgDetailsResult ? orgDetailsResult.data : null,
        orgDetailsError: 'error' in orgDetailsResult ? orgDetailsResult.error : null
      });
      
      const orgDetails = {
        data: orgData?.data ? {
          name: orgData.data.name,
          code_employeur: ('data' in orgDetailsResult && orgDetailsResult.data) ? orgDetailsResult.data.code_employeur || null : null,
          prenom_contact: ('data' in orgDetailsResult && orgDetailsResult.data) ? orgDetailsResult.data.prenom_contact || null : null,
          email_notifs: ('data' in orgDetailsResult && orgDetailsResult.data) ? orgDetailsResult.data.email_notifs || null : null,
          email_notifs_cc: ('data' in orgDetailsResult && orgDetailsResult.data) ? orgDetailsResult.data.email_notifs_cc || null : null
        } : null
      };
      
      console.log('🔍 [EMAIL] orgDetails final:', orgDetails);

      // 1. Email de notification à l'employeur (corrigé: utilise email_notifs au lieu de user.email)
      if (orgDetails?.data?.email_notifs) {
        const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000';
        
        // 🔒 SÉCURITÉ : Nettoyer et valider l'email principal
        const cleanAndValidateEmail = (email: string | null, fallback: string = 'paie@odentas.fr'): string => {
          if (!email) return fallback;
          
          const trimmed = email.trim().toLowerCase();
          
          // Valider le format email
          const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
          if (!emailRegex.test(trimmed)) {
            console.warn('⚠️ [SÉCURITÉ] Email invalide détecté, utilisation du fallback:', trimmed);
            return fallback;
          }
          
          return trimmed;
        };
        
        const emailNotifs = cleanAndValidateEmail(orgDetails.data.email_notifs);
        const emailNotifsCC = orgDetails.data.email_notifs_cc 
          ? cleanAndValidateEmail(orgDetails.data.email_notifs_cc, '') 
          : null;
        
        console.log('📧 [EMAIL] Envoi à email_notifs:', emailNotifs);
        console.log('📧 [EMAIL] CC à email_notifs_cc:', emailNotifsCC || 'Aucun');
        
        if (emailNotifs) {
          await sendUniversalEmailV2({
            type: 'employee-created',
            toEmail: emailNotifs,
            ccEmail: emailNotifsCC || undefined,
            data: {
              userName: orgDetails.data.prenom_contact || user?.user_metadata?.first_name || user?.user_metadata?.display_name?.split(' ')[0] || 'Cher client',
              companyName: orgDetails.data.name,
              employerCode: orgDetails.data.code_employeur || 'N/A',
              handlerName: 'Renaud BREVIERE-ABRAHAM',
              employeeName: data.salarie,
              email: data.adresse_mail,
              matricule: data.code_salarie,
              ctaUrl: `${baseUrl}/salaries/${data.id}`,
            }
          });
        }
      } else {
        console.warn('⚠️ [EMAIL] email_notifs non défini pour l\'organisation', orgId);
      }

      // 2. Générer token et envoyer invitation au salarié (nouveau)
      if (data.adresse_mail) {
        try {
          const result = await generateAutoDeclarationToken({
            salarie_id: data.id,
            send_email: true
          });

          if (result.success) {
            console.log('✅ [API /salaries POST] Token généré et invitation envoyée au salarié');
          } else {
            console.error('❌ [API /salaries POST] Erreur génération token:', result.error);
          }
        } catch (tokenError) {
          console.error('❌ [API /salaries POST] Erreur lors de la génération du token:', tokenError);
        }
      } else {
        console.warn('⚠️ [API /salaries POST] Pas d\'email pour le salarié, invitation non envoyée');
      }

    } catch (emailError) {
      console.error('📧 [API /salaries POST] Failed to send email notifications:', emailError);
      // Ne pas bloquer la réponse en cas d'échec des e-mails
    }

    return NextResponse.json({ ok: true, data }, { status: 201 });
  } catch (e: any) {
    console.error('💥 [API /salaries POST] unexpected', e?.message || e);
    return NextResponse.json({ ok: false, error: 'server_exception', detail: e?.message || String(e) }, { status: 500 });
  }
}

