// src/lib/analytics/enqueue.ts — enqueue an ETL job in the analytics service.
import "server-only";
import { env } from "@/env";
import type { CrawlOnpagePayload } from "@/lib/keywords/serp-types";
import type { SerpSemanticsPayload } from "@/lib/semantic/profile-types";

export type { CrawlOnpagePayload };

/** CSV-import job (legacy default) — carries the uploaded object + provider. */
type CsvImportInput = {
  type?: "csv-import";
  siteId: string;
  source: "semrush" | "mangools" | "ahrefs" | "seranking";
  objectKey: string;
  /** Language chosen at import (CSV carries none for Semrush/SE-Ranking). */
  lang?: string;
  destination: "reservoir" | "recherche";
};

/** Site-crawl job (module Pages) — no imported file, no source. The service
 *  discovers the sitemap from `site.url` and refuses a second concurrent crawl. */
type SiteCrawlInput = {
  type: "site-crawl";
  siteId: string;
};

/** Serp-semantics job (module Recherche/mots-clés) — analyse sémantique d'une
 *  SERP curée. Le service refuse une seconde analyse active pour le même
 *  `keywordId` (voir services/analytics/src/index.ts). */
type SerpSemanticsInput = {
  type: "serp-semantics";
  siteId: string;
  payload: SerpSemanticsPayload;
};

/** Crawl-onpage job (module Recherche/mots-clés) — re-crawle les compétiteurs
 *  SERP d'un mot-clé via Firecrawl. Le service refuse un second crawl actif
 *  pour le même `keywordId` (voir services/analytics/src/index.ts). */
type CrawlOnpageInput = {
  type: "crawl-onpage";
  siteId: string;
  payload: CrawlOnpagePayload;
};

/** Keyword-enrich job (réservoir/mots-clés) — enrichit une sélection de
 *  mots-clés via un connecteur BYOK (Mangools). Le service refuse un second
 *  enrichissement actif pour le même `siteId`+`provider` (voir
 *  services/analytics/src/index.ts). */
type KeywordEnrichInput = {
  type: "keyword-enrich";
  siteId: string;
  payload: { provider: "mangools"; keywordIds: string[] };
};

export type EnqueueInput =
  | CsvImportInput
  | SiteCrawlInput
  | SerpSemanticsInput
  | CrawlOnpageInput
  | KeywordEnrichInput;

/**
 * Erreur levée par `enqueueAnalyticsJob` quand le service HTTP répond en
 * erreur. Porte le status HTTP (`409` = job déjà `queued|running` pour la
 * même clé côté service — garde anti-doublon site-crawl/serp-semantics) pour
 * que les appelants puissent le discriminer sans parser le message.
 */
export class AnalyticsJobError extends Error {
  readonly status: number;
  constructor(message: string, status: number) {
    super(message);
    this.name = "AnalyticsJobError";
    this.status = status;
  }
}

/**
 * POST a job to the analytics service. The service inserts the `analytics_jobs`
 * row (status `queued`) and returns `{ id }`. Plain typed fetch on purpose — we
 * never import the service's Hono client (it would pull the DuckDB graph in).
 */
export async function enqueueAnalyticsJob(
  input: EnqueueInput,
): Promise<string> {
  if (!env.ANALYTICS_URL) {
    throw new Error("ANALYTICS_URL is not configured");
  }
  // The service switches on `type`; make it explicit even for legacy csv jobs
  // that historically omitted it (schema default is 'csv-import').
  const body =
    input.type === "site-crawl"
      ? { type: "site-crawl" as const, siteId: input.siteId }
      : input.type === "serp-semantics"
        ? {
            type: "serp-semantics" as const,
            siteId: input.siteId,
            payload: input.payload,
          }
        : input.type === "crawl-onpage"
          ? {
              type: "crawl-onpage" as const,
              siteId: input.siteId,
              payload: input.payload,
            }
          : input.type === "keyword-enrich"
            ? {
                type: "keyword-enrich" as const,
                siteId: input.siteId,
                payload: input.payload,
              }
            : { ...input, type: "csv-import" as const };
  const res = await fetch(`${env.ANALYTICS_URL}/jobs`, {
    method: "POST",
    headers: {
      "content-type": "application/json",
      // Bearer auth when the shared secret is configured (must match the
      // ANALYTICS_API_KEY set on the analytics service).
      ...(env.ANALYTICS_API_KEY
        ? { authorization: `Bearer ${env.ANALYTICS_API_KEY}` }
        : {}),
    },
    body: JSON.stringify(body),
  });
  if (!res.ok) {
    throw new AnalyticsJobError(
      `Analytics service rejected the job (${res.status} ${res.statusText})`,
      res.status,
    );
  }
  const data = (await res.json()) as { id?: string };
  if (!data.id) {
    throw new Error("Analytics service did not return a job id");
  }
  return data.id;
}
