// src/lib/connectors/core.ts — couche action org-scopée du connecteur Mangools
// (BYOK gratuit) : connect / disconnect / status, avec DI (`deps`) pour les
// tests. `server-only` (PAS "use server") : ce module n'est jamais exposé au
// réseau, donc `deps` ne peut pas être injecté par un appelant malveillant. Les
// wrappers exportés par actions.ts ("use server") n'exposent AUCUN paramètre
// `deps` — les arguments d'une Server Action sont contrôlés par le client :
// injecter `canManage`/`ping` depuis là contournerait la gate owner/admin ou
// stockerait un token jamais validé (même finding de sécurité Critical que
// SE-Ranking, Task 5). Pas d'add-on ni de billing : la feature est GRATUITE.
import "server-only";
import { revalidatePath } from "next/cache";
import { getActiveOrgId, getActiveUserId } from "@/lib/active-org";
import { createMangoolsClient } from "@/lib/mangools/client";
import { canManageOrgConnectors } from "./access";
import { getConnectorStatus, removeConnector, setConnector } from "./store";
import type { ConnectorStatus } from "./types";

const PROVIDER = "mangools" as const;

export type ConnectMangoolsResult =
  | { ok: true }
  | { ok: false; error: "invalid_token" | "forbidden" };

export type DisconnectMangoolsResult =
  | { ok: true }
  | { ok: false; error: "forbidden" };

type BaseDeps = {
  /** Gate owner/admin sur les connecteurs de l'org (canManageOrgConnectors). */
  canManage?: () => Promise<boolean>;
  orgId?: () => Promise<string>;
};

type ConnectDeps = BaseDeps & {
  userId?: () => Promise<string>;
  /** Validation du token — défaut = `searchLocations("France")` doit renvoyer
   *  ≥1 résultat (léger, ne consomme pas de lookup keywords). Throw = token
   *  refusé (l'appelant traduit en `invalid_token`). */
  ping?: (token: string) => Promise<void>;
};

type StatusDeps = {
  orgId?: () => Promise<string>;
};

/** Ping par défaut : recherche de localisation « France ». Un token invalide
 *  fait throw le client (MangoolsApiError 401/403…) ; un résultat vide est
 *  traité comme un échec. Aucun token ne fuite dans les messages d'erreur. */
async function defaultPing(token: string): Promise<void> {
  const results = await createMangoolsClient(token).searchLocations("France");
  if (results.length === 0) throw new Error("mangools_ping_empty");
}

function tryRevalidate(): void {
  try {
    // Requires a request/static-generation scope; skip outside one (tests).
    revalidatePath("/settings/integrations");
  } catch {}
}

/**
 * Branche (ou re-branche / remplace) le token Mangools de l'org. Gate :
 * owner|admin (`canManageOrgConnectors`). Le token est TRIMÉ ; vide →
 * `invalid_token` (jamais stocké). Le `ping` de validation tourne AVANT tout
 * stockage : sur échec on renvoie `invalid_token` et l'ancien token éventuel
 * reste intact (aucun `setConnector` n'est appelé). Sur succès, `setConnector`
 * chiffre et (re)passe le connecteur en `active` — le même flux sert donc à
 * l'ajout comme au remplacement.
 */
export async function connectMangools(
  token: string,
  deps?: ConnectDeps,
): Promise<ConnectMangoolsResult> {
  const canManage = deps?.canManage ?? canManageOrgConnectors;
  const orgId = deps?.orgId ?? getActiveOrgId;
  const userId = deps?.userId ?? getActiveUserId;
  const ping = deps?.ping ?? defaultPing;

  if (!(await canManage())) return { ok: false, error: "forbidden" };

  const trimmed = token.trim();
  if (trimmed === "") return { ok: false, error: "invalid_token" };

  try {
    await ping(trimmed);
  } catch {
    return { ok: false, error: "invalid_token" };
  }

  const [org, user] = await Promise.all([orgId(), userId()]);
  await setConnector(org, PROVIDER, trimmed, user);
  tryRevalidate();
  return { ok: true };
}

/**
 * Débranche le connecteur Mangools (supprime la ligne → état absent). Gate :
 * owner|admin. No-op inoffensif si absent.
 */
export async function disconnectMangools(
  deps?: BaseDeps,
): Promise<DisconnectMangoolsResult> {
  const canManage = deps?.canManage ?? canManageOrgConnectors;
  const orgId = deps?.orgId ?? getActiveOrgId;

  if (!(await canManage())) return { ok: false, error: "forbidden" };

  await removeConnector(await orgId(), PROVIDER);
  tryRevalidate();
  return { ok: true };
}

/** État du connecteur Mangools de l'org (sans jamais exposer le token). Ne
 *  re-ping pas l'API : lit le status stocké (`active` / `invalid` / absent). */
export async function getMangoolsStatus(
  deps?: StatusDeps,
): Promise<ConnectorStatus> {
  const orgId = deps?.orgId ?? getActiveOrgId;
  return getConnectorStatus(await orgId(), PROVIDER);
}
