// Types du SDK caisse.bzh 1.2.0 — https://caisse.bzh/developpeurs
// À placer à côté de caisse-bzh.mjs (même nom de base) pour l'autocomplétion TypeScript.

export declare const SDK_VERSION: string;
export declare const PROTOCOL_VERSION: 1;
export declare const DEFAULT_BASE_URL: string;

export declare class CaisseApiError extends Error {
  status: number;
  /** Code d'erreur renvoyé par l'API (`unknown_shop`, `no_items`, `bridge_auth`…). */
  code: string;
  body: unknown;
}

type FetchLike = (input: string, init?: { method?: string; headers?: Record<string, string>; body?: string }) => Promise<{ ok: boolean; status: number; text(): Promise<string> }>;

export interface ClientOptions {
  /** Par défaut https://caisse.bzh */
  baseUrl?: string;
  fetch?: FetchLike;
}

// ── API publique ───────────────────────────────────────────────────────────

export type DayKey = "mon" | "tue" | "wed" | "thu" | "fri" | "sat" | "sun";
export interface WeekHours {
  tz: string;
  days: Record<DayKey, { closed: boolean; ranges: [string, string][] }>;
}

export interface Carte {
  ok: true;
  shop: { slug: string; name: string; tagline: string; address: string; phone: string; currency: string };
  hours: WeekHours;
  /** Ouvert à l'instant, dans le fuseau de l'établissement. */
  openNow: boolean;
  categories: { id: string; name: string; icon: string }[];
  items: { id: string; categoryId: string; name: string; /** € TTC */ price: number; description?: string; tags?: string[] }[];
}

export interface OrderInput {
  slug: string;
  customerName: string;
  phone?: string;
  /** "HH:MM" ou un libellé court ; vide = « Dès que possible ». */
  pickupAt?: string;
  note?: string;
  /** 40 lignes au plus, quantité 1 à 20. Un id inconnu est ignoré. */
  items: { itemId: string; qty?: number; note?: string }[];
}

export interface PlacedOrder {
  id: string;
  /** Numéro du jour, affiché en caisse. */
  no: number;
  pickupAt: string;
  /** € TTC, recalculé par la caisse. */
  total: number;
  status: "nouveau";
  payment: { method: "onsite"; status: "pending" };
}

/** Même clé pour un nouvel essai : la première réponse est rendue, sans doublon (24 h). */
export interface IdempotencyOptions { idempotencyKey?: string }

export interface Client {
  getCarte(slug: string): Promise<Carte>;
  placeOrder(input: OrderInput, options?: IdempotencyOptions): Promise<PlacedOrder>;
}

export declare function createClient(options?: ClientOptions): Client;

// ── API avec clé ───────────────────────────────────────────────────────────

export interface ApiClientOptions extends ClientOptions {
  /** cbz_live_…, créée dans la console, page « Accès API ». Côté serveur uniquement. */
  apiKey: string;
}

export interface ApiShop {
  ok: true;
  shop: { id: string; slug: string; name: string; kind: "restaurant" | "bar" | "boutique" | "alimentaire"; city: string; address: string; phone: string; siret: string; vatNumber: string };
  features: { onlineOrders: boolean; stock: boolean };
  key: { id: string; name: string; scope: "read" | "write" };
}

export interface Category { id: string; name: string; icon: string; course: "starter" | "main" | "dessert" | "drink"; /** 0.2 pour 20 % */ vatRate: number }

export interface Item {
  id: string;
  categoryId: string;
  name: string;
  /** € TTC */
  price: number;
  description?: string;
  tags?: string[];
  sku?: string;
  barcode?: string;
  variants?: { id: string; name: string; price?: number; sku?: string; barcode?: string; syncKey?: string }[];
  modifiers?: { id: string; name: string; required: boolean; multiple: boolean; options: { id: string; name: string; price: number }[] }[];
  syncKey?: string;
  /** "sync" : l'article vient de la boutique en ligne et ne se modifie que là-bas. */
  managedBy: "caisse" | "sync";
}

export interface ItemInput {
  name: string;
  price: number;
  categoryId: string;
  description?: string;
  tags?: string[];
  sku?: string;
  barcode?: string;
}

export interface StockLine { syncKey: RecordKey; quantity: number | null; itemId?: string; variantId?: string; name?: string; sku?: string }

export interface Sale {
  id: string;
  number: number;
  fiscalNumber: number | null;
  day: string;
  settledAt: string;
  settledBy: string;
  type: "table" | "counter";
  label: string;
  covers: number;
  subtotal: number;
  discount: number;
  total: number;
  tip: number;
  loyaltyRedemption: number;
  vat: { rate: number; gross: number; net: number; vat: number }[];
  payments: { method: string; amount: number }[];
  items: { name: string; qty: number; unitPrice: number; total?: number; vatRate?: number; itemId?: string; variantId?: string; sku?: string }[];
  /** Vente du mode formation, hors chiffre d'affaires. */
  training: boolean;
}

export type OrderStatus = "new" | "accepted" | "ready" | "collected" | "cancelled";

export interface Order {
  id: string;
  number: number;
  status: OrderStatus;
  customerName: string;
  phone: string;
  pickupAt: string;
  note: string;
  items: { itemId: string; name: string; qty: number; unitPrice: number; note: string }[];
  total: number;
  payment: { method: "onsite" | "card"; status: "pending" | "paid" };
  createdAt: string;
}

export interface ApiClient {
  getShop(): Promise<ApiShop>;
  getCatalogue(): Promise<{ ok: true; categories: Category[]; items: Item[] }>;
  getItem(id: string): Promise<Item>;
  createItem(item: ItemInput, options?: IdempotencyOptions): Promise<Item>;
  updateItem(id: string, patch: Partial<ItemInput>): Promise<Item>;
  deleteItem(id: string): Promise<{ ok: true; deleted: string }>;
  getStock(): Promise<StockLine[]>;
  listSales(params?: { from?: string; to?: string; limit?: number }): Promise<{ ok: true; count: number; hasMore: boolean; sales: Sale[] }>;
  listOrders(params?: { status?: OrderStatus }): Promise<Order[]>;
  updateOrder(id: string, action: "accept" | "ready" | "collected" | "cancel"): Promise<Order>;
}

export declare function createApiClient(options: ApiClientOptions): ApiClient;

// ── synchronisation ────────────────────────────────────────────────────────

/** "woo:product:<id>" ou "woo:variant:<id>", id entier positif de votre boutique. */
export type RecordKey = string;

export interface Prices {
  regular?: number | string | null;
  sale?: number | string | null;
  /** Taux de TVA en pourcentage (20, 10, 5.5). */
  tax_rate?: number | null;
  /** "gross" (TTC, par défaut) ou "net" (HT, la caisse ajoute la TVA). */
  basis?: "gross" | "net";
}

export interface ProductSnapshot {
  key: RecordKey;
  name?: string;
  /** Seul "publish" garde l'article en caisse ; toute autre valeur le retire. */
  status?: "publish" | string;
  deleted?: boolean;
  archived?: boolean;
  sku?: string;
  /** Chemins de catégories, du plus général au plus précis : [["Vêtements", "Pantalons"]]. */
  categories?: string[][];
  prices?: Prices;
  identifiers?: { ean13?: string; gtin?: string; upc?: string };
  variants?: {
    key: RecordKey;
    status: "publish" | string;
    attributes?: Record<string, string>;
    sku?: string;
    prices?: Prices;
    identifiers?: { ean13?: string; gtin?: string; upc?: string };
  }[];
  /** Quantités de départ (ignorées pour une clé déjà connue : ensuite seuls les événements de stock comptent). */
  inventory?: { key: RecordKey; quantity: number | null }[];
}

export type SyncEvent =
  | { id?: string; kind: "product"; key: RecordKey; payload: { data: ProductSnapshot } }
  | { id?: string; kind: "stock"; key: RecordKey; payload: { delta: number } | { set_mode: true; quantity: number | null } }
  | { id?: string; kind: "order" | "customer"; key?: string; payload: Record<string, unknown> };

export interface SyncHealth {
  ok: true;
  protocol: 1;
  platform: "ps";
  software: "caisse.bzh";
  mode: "live" | "disabled";
  diagnostics: { ok: boolean; mode: string; queue: { direction: "out"; state: "pending"; count: number }[]; issues: unknown[] };
}

export interface SyncClientOptions extends ClientOptions {
  /** Jeton de la boutique (24 hex), page « Boutique en ligne » de la console. */
  token: string;
  /** Secret partagé (64 hex), même page. Côté serveur uniquement. */
  secret: string;
}

export interface SyncClient {
  health(): Promise<SyncHealth>;
  sendEvents(events: SyncEvent[]): Promise<{ ok: true; accepted: number }>;
  upsertProduct(product: ProductSnapshot, id?: string): Promise<{ ok: true; accepted: number }>;
  removeProduct(key: RecordKey, id?: string): Promise<{ ok: true; accepted: number }>;
  adjustStock(key: RecordKey, delta: number, id?: string): Promise<{ ok: true; accepted: number }>;
  setStock(key: RecordKey, quantity: number | null, id?: string): Promise<{ ok: true; accepted: number }>;
  tick(): Promise<{ ok: true }>;
}

export declare function createSyncClient(options: SyncClientOptions): SyncClient;

/** Message signé que la caisse envoie à votre webhook. */
export type TillMessage =
  | { version: 1; source: "ps"; op: "events"; events: { id: string; kind: "stock"; key: RecordKey; payload: { delta: number } }[] }
  | { version: 1; source: "ps"; op: "tick" };

export declare function sign(secret: string, body: string, timestamp: string, nonce: string): Promise<string>;
export declare function signedHeaders(secret: string, body: string): Promise<Record<string, string>>;
export declare function verifySignature(input: {
  secret: string;
  /** Corps brut, avant JSON.parse. */
  body: string;
  headers: Headers | Record<string, string | string[] | undefined>;
  toleranceSeconds?: number;
}): Promise<TillMessage>;
export declare function newEventId(): string;
export declare function randomHex(bytes: number): string;
