import type { Settings, ZElement } from '../core/types';
import { ApiError, isSessionExpired, refreshNonce, SESSION_EXPIRED_MESSAGE, type RestConfig } from './client';

/** Kinds of templates: a section (one or more top-level containers) or a whole page. */
export type LibraryType = 'section' | 'page';

/** A saved template ("My templates", post type `zadex_library`). */
export interface LibraryItem {
  id: number;
  title: string;
  type: LibraryType;
  tags: string[];
  /** Featured image URL ('' when none). */
  thumbnail: string;
  date: string;
  author: string;
}

export interface LibraryItemFull extends LibraryItem {
  elements: ZElement[];
  css: string;
  pageSettings: Settings;
}

/** A starter kit from `library/kits.json` (or the remote index). `thumbnail` is inline SVG markup. */
export interface KitSummary {
  slug: string;
  title: string;
  type: LibraryType;
  tags: string[];
  thumbnail: string;
}

export interface Kit extends KitSummary {
  elements: ZElement[];
  pageSettings: Settings;
}

/** The page JSON format (`GET /documents/{id}/export`, the Library "Import" tab). */
export interface PageExport {
  version: number;
  generator?: string;
  title?: string;
  elements: ZElement[];
  pageSettings: Settings;
  css: string;
  template?: string;
}

export interface SaveTemplateBody {
  title: string;
  type: LibraryType;
  elements: ZElement[];
  css?: string;
  pageSettings?: Settings;
  tags?: string[];
}

export interface LibraryApi {
  list(type?: LibraryType): Promise<LibraryItem[]>;
  get(id: number): Promise<LibraryItemFull>;
  save(body: SaveTemplateBody): Promise<{ id: number; item: LibraryItem }>;
  remove(id: number): Promise<{ deleted: boolean; id: number }>;
  kits(): Promise<KitSummary[]>;
  kit(slug: string): Promise<Kit>;
  /** Sanitizes an uploaded page export on the server. */
  importPage(json: unknown): Promise<PageExport>;
  exportPage(postId: number): Promise<PageExport>;
}

type Method = 'GET' | 'POST' | 'DELETE';

/** REST implementation (zadex/v1, cookie auth + X-WP-Nonce), same error handling as the document client. */
export function createRestLibraryApi(config: RestConfig, fetchFn: typeof fetch = (...args) => fetch(...args)): LibraryApi {
  const base = config.restUrl.replace(/\/$/, '');
  async function call<T>(method: Method, path: string, body?: unknown, retried = false): Promise<T> {
    const res = await fetchFn(`${base}${path}`, {
      method,
      credentials: 'same-origin',
      headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': config.nonce },
      body: body === undefined ? undefined : JSON.stringify(body),
    });
    const text = await res.text();
    let json: any = null;
    try {
      json = text ? JSON.parse(text) : null;
    } catch {
      throw new ApiError(`Invalid JSON from ${path}`, res.status);
    }
    if (!res.ok) {
      const err = new ApiError(json?.message ?? `Request failed (${res.status})`, res.status, json?.code ?? '');
      if (isSessionExpired(err)) {
        if (!retried && (await refreshNonce(config, fetchFn))) return call<T>(method, path, body, true);
        err.message = SESSION_EXPIRED_MESSAGE;
      }
      throw err;
    }
    return json as T;
  }
  // WordPress may run without pretty permalinks (`?rest_route=/zadex/v1`): query args then join with `&`.
  const withQuery = (path: string, query: string): string => `${path}${base.includes('?') ? '&' : '?'}${query}`;
  return {
    list: async (type) => (await call<{ items: LibraryItem[] }>('GET', type ? withQuery('/library', `type=${type}`) : '/library')).items,
    get: (id) => call('GET', `/library/${id}`),
    save: (body) => call('POST', '/library', body),
    remove: (id) => call('DELETE', `/library/${id}`),
    kits: async () => (await call<{ kits: KitSummary[] }>('GET', '/library/remote')).kits,
    kit: (slug) => call('GET', `/library/remote/${encodeURIComponent(slug)}`),
    importPage: (json) => call('POST', '/library/import', json),
    exportPage: (postId) => call('GET', `/documents/${postId}/export`),
  };
}

let instance: LibraryApi | null = null;

export function setLibraryApi(api: LibraryApi | null): void {
  instance = api;
}

export async function getLibraryApi(): Promise<LibraryApi> {
  if (instance) return instance;
  const config = window.ZADEX;
  if (!config) throw new Error('No config');
  // The dev harness mock is development-only: the production build drops this branch and its chunk.
  if (import.meta.env.DEV && config.restUrl.startsWith('mock:')) {
    const { createMockLibraryApi } = await import('@/dev/mock-library');
    instance = createMockLibraryApi();
  } else {
    instance = createRestLibraryApi(config);
  }
  return instance;
}
