// Contexte inwink : expose l'objet courant (event / community / tenant) déduit
// de l'URL ou choisi par l'utilisateur, et les loaders de listes pour les pickers.
import { createContext, useMemo, useState, type PropsWithChildren } from 'react';
import { useInwinkCustomerApi } from '../hooks/useInwinkCustomerApi';
import {
  deriveScope,
  loadPersistedContext,
  persistContext,
  readInwinkContextFromUrl,
  type InwinkContextParams,
  type InwinkScope,
} from '../lib/inwinkContextParams';

/** Option affichable dans un picker (événement, communauté ou tenant). */
export interface InwinkContextOption {
  id: string;
  label: string;
}

export interface IInwinkContextQuery {
  search?: string;
  id?: string;
}

interface InwinkContextValue {
  scope: InwinkScope;
  eventId?: string;
  communityId?: string;
  tenantId?: string;
  /** Sélectionne un événement (réinitialise la communauté). */
  setEvent: (id: string) => void;
  /** Sélectionne une communauté (réinitialise l'événement). */
  setCommunity: (id: string) => void;
  /** Sélectionne un tenant / audience (réinitialise event + community). */
  setTenant: (id: string) => void;
  /**
   * Liste les événements de l'utilisateur (API Customer `me/events/query`),
   * triés par date décroissante. `query` filtre côté serveur par nom ou id.
   */
  loadEvents: (query?: IInwinkContextQuery) => Promise<InwinkContextOption[]>;
  /**
   * Liste les communautés de l'utilisateur (API Customer `me/communities/query`).
   * `query` filtre côté serveur par nom ou id.
   */
  loadCommunities: (query?: IInwinkContextQuery) => Promise<InwinkContextOption[]>;
  /**
   * Liste les tenants / audiences de l'utilisateur (API Customer `me/tenants/query`).
   * `query` filtre côté serveur par nom ou id.
   */
  loadTenants: (query?: IInwinkContextQuery) => Promise<InwinkContextOption[]>;
}

export const InwinkContext = createContext<InwinkContextValue | null>(null);

// Pagination des requêtes `*/query`. Les listes sont paginées côté serveur :
// la recherche (`query`) sert à retrouver un objet au-delà de la 1re page.
const PAGE = { index: 0, size: 200 };

// Construit le corps d'une requête de liste (`me/<entité>/query`) :
// - `query` → filtre serveur par id (égalité) OU libellé (`$contains`) ;
// - `labelField` → champ texte de l'entité (`title` pour les events, `name` sinon) ;
// - `orders` → tri serveur (ex. date décroissante pour les events).
// Ajuste `selects` / `filters` selon les entités après discovery MCP (cf. AGENTS.md).
function buildQueryBody(opts: {
  query?: IInwinkContextQuery;
  labelField: string;
  orders?: unknown[];
}): Record<string, unknown> {
  const body: Record<string, any> = { page: PAGE };
  if (opts.orders) body.orders = opts.orders;
  if (opts.query?.id) {
    body.filters ??= {};
    body.filters.id = opts.query.id;    
  } else if (opts.query?.search) {
    body.filters ??= {};
    const q = opts.query?.search?.trim();
    if (q) {
      body.filters.$and ??= [];
      //detect if q is a guid
      const guidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
      if (guidRegex.test(q)) {
        body.filters.$and.push({ id: q });
      } else {
        body.filters.$and.push({ [opts.labelField]: { $contains: q } });
      }
      
    }
  }
  return body;
}

function asRecord(value: unknown): Record<string, unknown> {
  return value && typeof value === 'object' ? (value as Record<string, unknown>) : {};
}

// Normalise une réponse `*/query` en options { id, label }, quel que soit le wrapping.
function normalizeOptions(raw: unknown): InwinkContextOption[] {
  const root = asRecord(raw);
  const list: unknown[] = Array.isArray(raw)
    ? raw
    : Array.isArray(root.data)
      ? (root.data as unknown[])
      : Array.isArray(root.items)
        ? (root.items as unknown[])
        : [];

  const options: InwinkContextOption[] = [];
  for (const entry of list) {
    const item = asRecord(entry);
    const id = item.id ?? item.eventId ?? item.communityId ?? item.tenantId;
    if (id == null) continue;
    const label = item.title ?? item.name ?? item.displayName ?? id;
    options.push({ id: String(id), label: String(label) });
  }
  return options;
}

/** Init : l'URL fait foi ; sinon on retombe sur le contexte persisté en session. */
function initialParams(): InwinkContextParams {
  const fromUrl = readInwinkContextFromUrl();
  if (fromUrl.eventId || fromUrl.communityId || fromUrl.tenantId) {
    persistContext(fromUrl);
    return fromUrl;
  }
  return loadPersistedContext();
}

export function InwinkContextProvider({ children }: PropsWithChildren) {
  const customerApi = useInwinkCustomerApi();
  const [params, setParams] = useState<InwinkContextParams>(initialParams);

  const value = useMemo<InwinkContextValue>(() => {
    const apply = (next: InwinkContextParams) => {
      setParams(next);
      persistContext(next);
    };
    return {
      scope: deriveScope(params),
      eventId: params.eventId,
      communityId: params.communityId,
      tenantId: params.tenantId,
      setEvent: (id) => apply({ ...params, eventId: id, communityId: undefined }),
      setCommunity: (id) => apply({ ...params, communityId: id, eventId: undefined }),
      setTenant: (id) => apply({ tenantId: id }),
      loadEvents: async (query?: IInwinkContextQuery) =>
        normalizeOptions(
          await customerApi.post(
            '/00000000-0000-0000-0000-000000000000/my/events/query',
            buildQueryBody({ query, labelField: 'title', orders: [{ desc: true, value: { startDate: {} } }] }),
          ),
        ),
      loadCommunities: async (query?: IInwinkContextQuery) =>
        normalizeOptions(
          await customerApi.post(
            '/00000000-0000-0000-0000-000000000000/my/communities/query',
            buildQueryBody({ query, labelField: 'name' }),
          ),
        ),
      loadTenants: async (query?: IInwinkContextQuery) =>
        normalizeOptions(
          await customerApi.post(
            '/00000000-0000-0000-0000-000000000000/my/tenants/query',
            buildQueryBody({ query, labelField: 'name' }),
          ),
        ),
    };
  }, [params, customerApi]);

  return <InwinkContext.Provider value={value}>{children}</InwinkContext.Provider>;
}
