// src/lib/semantic/actions.ts — action de déclenchement du job `serp-semantics`
// + lecteur du profil sémantique stocké (RSC/UI, S4). L'UI poll la progression
// via `getAnalyticsJob` (src/lib/analytics/actions.ts) — aucun nouveau canal ici.
"use server";
import { eq } from "drizzle-orm";
import { getActiveOrgId } from "@/lib/active-org";
import { enqueueAnalyticsJob } from "@/lib/analytics/enqueue";
import { db } from "@/lib/db/client";
import { keywords } from "@/lib/db/domain-schema";
import { fetchSerpViaMangools } from "@/lib/mangools/serp-fetch";
import { assertKeywordInActiveOrg } from "@/lib/sites/assert-site";
import type { StoredSemanticProfile } from "./profile-types";
import {
  buildSerpSemanticsPayload,
  mapEnqueueError,
  type SerpSemanticsOpts,
} from "./serp-payload";

/**
 * Hook D6 (design) — no-op aujourd'hui. Brancher quota/credits ici : refuser
 * le déclenchement quand l'org a épuisé son forfait d'analyses sémantiques.
 */
async function assertCanAnalyze(_orgId: string): Promise<void> {
  // TODO(D6): brancher quota/credits ici.
}

/**
 * Déclenche l'analyse sémantique de la SERP curée d'un mot-clé.
 * 1. Tenancy (`assertKeywordInActiveOrg`) → siteId.
 * 2. Garde quota/credits (D6, no-op).
 * 3. Lit le snapshot SERP du pays de référence ; absent, ou 0 URL après
 *    curation/clamp → `no_serp`.
 * 4. Enqueue du job ; 409 (déjà en cours) → `already_running`, autre échec
 *    → `enqueue_failed` (logué).
 */
export async function startSerpSemanticsAction(
  keywordId: string,
  opts?: SerpSemanticsOpts,
): Promise<
  | { ok: true; jobId: string }
  | { ok: false; error: "no_serp" | "already_running" | "enqueue_failed" }
> {
  const siteId = await assertKeywordInActiveOrg(keywordId);
  const orgId = await getActiveOrgId();
  await assertCanAnalyze(orgId);

  const [row] = await db
    .select({ serp: keywords.serp, country: keywords.country })
    .from(keywords)
    .where(eq(keywords.id, keywordId));
  if (!row) return { ok: false, error: "no_serp" };

  let payload = buildSerpSemanticsPayload(keywordId, row, opts);
  if (!payload || payload.urls.length === 0) {
    // Task 9 — SERP auto : aucune SERP curée pour la locale de référence. Si un
    // connecteur Mangools est actif, on tente une récupération auto (best-effort,
    // taxonomie d'erreur interne au module) puis on RELIT le snapshot fraîchement
    // mergé pour reconstruire le payload. Tout échec (pas de connecteur, quota,
    // etc.) → `no_serp` INCHANGÉ : le fallback import manuel reste intact et
    // aucun nouveau code d'erreur n'est exposé à l'UI.
    const fetched = await fetchSerpViaMangools(keywordId);
    if (!fetched.ok) return { ok: false, error: "no_serp" };
    const [refetched] = await db
      .select({ serp: keywords.serp, country: keywords.country })
      .from(keywords)
      .where(eq(keywords.id, keywordId));
    const retryPayload = refetched
      ? buildSerpSemanticsPayload(keywordId, refetched, opts)
      : null;
    if (!retryPayload || retryPayload.urls.length === 0) {
      return { ok: false, error: "no_serp" };
    }
    payload = retryPayload;
  }

  try {
    const jobId = await enqueueAnalyticsJob({
      type: "serp-semantics",
      siteId,
      payload,
    });
    return { ok: true, jobId };
  } catch (err) {
    const error = mapEnqueueError(err);
    if (error === "enqueue_failed") {
      console.error("startSerpSemanticsAction: enqueue failed", err);
    }
    return { ok: false, error };
  }
}

/**
 * Lecture du profil sémantique stocké (RSC/UI) — tenancy incluse. `null`
 * quand aucune analyse n'a encore été mergée pour le pays de référence.
 */
export async function getSemanticProfileAction(
  keywordId: string,
): Promise<StoredSemanticProfile | null> {
  await assertKeywordInActiveOrg(keywordId);
  const [row] = await db
    .select({ semantic: keywords.semantic, country: keywords.country })
    .from(keywords)
    .where(eq(keywords.id, keywordId));
  if (!row) return null;
  return row.semantic?.[row.country] ?? null;
}
