// src/lib/keywords/search-params.ts — nuqs parsers shared between the
// réservoir/recherche RSC pages (server cache) and the client components
// (useQueryState reuses the very same parser objects), plus the pure
// derivations (`filterFromSearchParams`, sort codec) both sides need to agree
// on the same URL shape. `nuqs/server` parsers carry no client-only code, so
// they're safe to import from either a Server or Client Component.
import {
  createSearchParamsCache,
  parseAsBoolean,
  parseAsString,
  parseAsStringEnum,
} from "nuqs/server";
import type { KeywordSort, KeywordSortField } from "./list-query";
import type { KeywordStatus, SearchIntent } from "./types";

const csvParam = parseAsString
  .withDefault("")
  .withOptions({ clearOnDefault: true });

/** Parsers for the réservoir's URL-owned facets + sort. Recherche (T4b) adds
 *  its own (`gsc`, `view`) on top of these. */
export const reservoirParsers = {
  q: csvParam,
  status: csvParam,
  theme: csvParam,
  intent: csvParam,
  lang: csvParam,
  country: csvParam,
  sort: parseAsString.withDefault("term.asc").withOptions({
    clearOnDefault: true,
  }),
  /** Facette « Sans page » (module Pages) — toggle booléen, drive
   *  `KeywordListFilter.noTargetPage`. Absent de l'URL = false (clearOnDefault). */
  nopage: parseAsBoolean.withDefault(false).withOptions({
    clearOnDefault: true,
  }),
};

/** Server-side cache (RSC) — `.parse(searchParams)` then `.get(key)`. */
export const reservoirSearchParamsCache =
  createSearchParamsCache(reservoirParsers);

const GSC_BAND_VALUES = ["all", "top10", "quickwin", "beyond", "none"] as const;

/** Onglets de vue de la Recherche (« Tous · Shortlist · Exclus ·
 *  Quick-wins GSC »). */
const RESEARCH_TABS = ["tous", "shortlist", "exclus", "quickwins"] as const;
export type ResearchTab = (typeof RESEARCH_TABS)[number];

/** Recherche's parsers: the réservoir's facets/sort + the GSC positioning
 *  band (URL-owned, drives `filter.gscBand`) + the view tab (`tab`, drives
 *  `filter.shortlisted` — parsed by the RSC page too so a deep link
 *  `?tab=shortlist` seeds the right initial page). `view` (list/matrix) stays
 *  a plain local `useQueryState` in the container — it never enters
 *  `KeywordListFilter`, so the RSC page doesn't need to parse it. */
export const researchParsers = {
  ...reservoirParsers,
  gsc: parseAsStringEnum([...GSC_BAND_VALUES])
    .withDefault("all")
    .withOptions({ clearOnDefault: true }),
  tab: parseAsStringEnum([...RESEARCH_TABS])
    .withDefault("tous")
    .withOptions({ clearOnDefault: true }),
};

export const researchSearchParamsCache =
  createSearchParamsCache(researchParsers);

const SORT_FIELDS: readonly KeywordSortField[] = [
  "term",
  "volume",
  "kd",
  "cpc",
  "growth",
  "gscPosition",
  "gscOpportunity",
];

/** Decodes the `sort` URL value (`"<field>.asc|desc"`). Malformed or unknown
 *  input falls back to the default sort (`term` ascending) rather than
 *  throwing — a stale/hand-edited URL should never break the page. */
export function parseSort(raw: string): KeywordSort {
  const [field, dir] = raw.split(".");
  if ((SORT_FIELDS as string[]).includes(field)) {
    return { field: field as KeywordSortField, desc: dir === "desc" };
  }
  return { field: "term", desc: false };
}

export function serializeSort(sort: KeywordSort): string {
  return `${sort.field}.${sort.desc ? "desc" : "asc"}`;
}

function csvToArray(v: string): string[] {
  return v ? v.split(",").filter(Boolean) : [];
}

/** Given a site's primary language and the languages actually present
 *  (facets), returns the language to pre-select — mirrors the old
 *  `deriveLanguageFilter`'s rule: default to the primary language only when
 *  it's actually present in the dataset, otherwise no filter. */
export function resolveDefaultLang(
  primaryLanguage: string | undefined,
  availableLanguages: string[],
): string {
  return primaryLanguage && availableLanguages.includes(primaryLanguage)
    ? primaryLanguage
    : "";
}

/** Pure: raw URL strings → `KeywordListFilter` facet fields (q/status/theme/
 *  intent/lang). Numeric ranges and `source` are UI-local (not URL-owned) and
 *  are merged in by the caller. `lang` is taken literally — resolve its
 *  default (via `resolveDefaultLang`) before calling this, so an explicit
 *  "toutes les langues" clear is never silently re-defaulted. */
export function filterFromSearchParams(params: {
  q: string;
  status: string;
  theme: string;
  intent: string;
  lang: string;
  country: string;
  /** Facette « Sans page » (optionnel — les appelants recherche/réservoir la
   *  passent via le spread des params URL). */
  nopage?: boolean;
}): {
  q?: string;
  status?: KeywordStatus[];
  theme?: string[];
  intent?: SearchIntent[];
  lang?: string;
  countries?: string[];
  noTargetPage?: boolean;
} {
  const filter: {
    q?: string;
    status?: KeywordStatus[];
    theme?: string[];
    intent?: SearchIntent[];
    lang?: string;
    countries?: string[];
    noTargetPage?: boolean;
  } = {};
  const q = params.q.trim();
  if (q) filter.q = q;
  if (params.status)
    filter.status = csvToArray(params.status) as KeywordStatus[];
  if (params.theme) filter.theme = csvToArray(params.theme);
  if (params.intent)
    filter.intent = csvToArray(params.intent) as SearchIntent[];
  if (params.lang) filter.lang = params.lang;
  if (params.country) filter.countries = csvToArray(params.country);
  if (params.nopage) filter.noTargetPage = true;
  return filter;
}
