import { useCallback } from 'react';
import type { CbTemplate, CbTemplateList, CbTemplateCategories, CbTemplateWithData, CbRevision, CbRevisionWithData, PageData, DesignSystem, JalpiClass } from '../types';

declare const jalpiEditorConfig: {
  postId: number;
  apiUrl: string;
  nonce: string;
};

async function apiFetch<T>(url: string, options: RequestInit = {}): Promise<T> {
  const res = await fetch(url, {
    ...options,
    headers: {
      'Content-Type': 'application/json',
      'X-WP-Nonce': jalpiEditorConfig.nonce,
      ...(options.headers ?? {}),
    },
  });

  if (!res.ok) {
    const err = await res.json().catch(() => ({ message: res.statusText }));
    throw new Error(err.message ?? 'API Error');
  }

  return res.json();
}

export function useApi() {
  const base = jalpiEditorConfig.apiUrl;
  const postId = jalpiEditorConfig.postId;

  const loadPage = useCallback(async (): Promise<{ page_data: PageData; post_title: string }> => {
    return apiFetch(`${base}/page/${postId}`);
  }, [base, postId]);

  const savePage = useCallback(async (pageData: PageData): Promise<{ success: boolean }> => {
    return apiFetch(`${base}/page/${postId}`, {
      method: 'POST',
      body: JSON.stringify({ page_data: pageData }),
    });
  }, [base, postId]);

  const loadDesignSystem = useCallback(async (): Promise<DesignSystem> => {
    return apiFetch(`${base}/design-system`);
  }, [base]);

  const saveDesignSystem = useCallback(async (designSystem: DesignSystem): Promise<{ success: boolean; design_system: DesignSystem }> => {
    return apiFetch(`${base}/design-system`, {
      method: 'POST',
      body: JSON.stringify({ design_system: designSystem }),
    });
  }, [base]);

  const publishPage = useCallback(async (): Promise<{ success: boolean; permalink: string }> => {
    return apiFetch(`${base}/page/${postId}/publish`, {
      method: 'POST',
      body: JSON.stringify({}),
    });
  }, [base, postId]);

  const getMedia = useCallback(async () => {
    return apiFetch<Array<{ id: number; url: string; alt: string; title: string }>>(`${base}/media`);
  }, [base]);

  const uploadMedia = useCallback(async (file: File) => {
    const form = new FormData();
    form.append('file', file);
    const res = await fetch(`${base}/media/upload`, {
      method: 'POST',
      headers: { 'X-WP-Nonce': jalpiEditorConfig.nonce },
      body: form,
    });
    if (!res.ok) throw new Error('Upload failed');
    return res.json() as Promise<{ id: number; url: string; alt: string }>;
  }, [base]);

  const seedBundledTemplates = useCallback(async (force = false): Promise<{ seeded: number; skipped: number }> => {
    return apiFetch(`${base}/templates/seed-bundled`, {
      method: 'POST',
      body: JSON.stringify({ force }),
    });
  }, [base]);

  const getTemplates = useCallback(async (params?: {
    search?: string; category?: string; type?: string;
    tag?: string; source?: string; page?: number; per_page?: number;
  }): Promise<CbTemplateList> => {
    const qs = new URLSearchParams();
    if (params?.search)   qs.set('search', params.search);
    if (params?.category) qs.set('category', params.category);
    if (params?.type)     qs.set('type', params.type);
    if (params?.tag)      qs.set('tag', params.tag);
    if (params?.source)   qs.set('source', params.source);
    if (params?.page)     qs.set('page', String(params.page));
    if (params?.per_page) qs.set('per_page', String(params.per_page));
    const query = qs.toString();
    return apiFetch(`${base}/templates${query ? '?' + query : ''}`);
  }, [base]);

  const getTemplateCategories = useCallback(async (): Promise<CbTemplateCategories> => {
    return apiFetch(`${base}/templates/categories`);
  }, [base]);

  /**
   * Fetch one template with its data. Kit templates are virtual and live on a
   * separate read-only route, so route on the id shape.
   */
  const getTemplate = useCallback(async (id: number | string): Promise<CbTemplateWithData> => {
    const str = String(id);
    // Every virtual kit id (kit: / kitsec: / kitchrome:) resolves through one
    // route. The id contains ':' so it is base64url-encoded for the path.
    if (/^kit(sec|chrome)?:/.test(str)) {
      const eid = btoa(str).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
      return apiFetch(`${base}/kit-template/${eid}`);
    }
    return apiFetch(`${base}/templates/${id}`);
  }, [base]);

  const getTemplateUserLists = useCallback(async (): Promise<{ favorites: string[]; recent: string[] }> => {
    return apiFetch(`${base}/templates/user-lists`);
  }, [base]);

  const toggleTemplateFavorite = useCallback(async (id: number | string): Promise<{ favorites: string[]; favorited: boolean }> => {
    return apiFetch(`${base}/templates/favorite`, {
      method: 'POST',
      body: JSON.stringify({ id: String(id) }),
    });
  }, [base]);

  const pushTemplateRecent = useCallback(async (id: number | string): Promise<{ recent: string[] }> => {
    return apiFetch(`${base}/templates/recent`, {
      method: 'POST',
      body: JSON.stringify({ id: String(id) }),
    });
  }, [base]);

  const createTemplate = useCallback(async (data: {
    name: string;
    description?: string;
    category?: string;
    type: 'page' | 'section' | 'element';
    data: unknown;
  }): Promise<CbTemplateWithData> => {
    return apiFetch(`${base}/templates`, { method: 'POST', body: JSON.stringify(data) });
  }, [base]);

  const deleteTemplate = useCallback(async (id: number | string): Promise<{ deleted: boolean }> => {
    return apiFetch(`${base}/templates/${id}`, { method: 'DELETE' });
  }, [base]);

  const duplicateTemplate = useCallback(async (id: number | string): Promise<CbTemplateWithData> => {
    return apiFetch(`${base}/templates/${id}/duplicate`, { method: 'POST', body: '{}' });
  }, [base]);

  const exportTemplate = useCallback(async (id: number | string): Promise<Record<string, unknown>> => {
    return apiFetch(`${base}/templates/${id}/export`);
  }, [base]);

  const importTemplate = useCallback(async (json: string, name?: string): Promise<CbTemplateWithData> => {
    return apiFetch(`${base}/templates/import`, {
      method: 'POST',
      body: JSON.stringify({ json, name: name ?? '' }),
    });
  }, [base]);

  const listRevisions = useCallback(async (): Promise<CbRevision[]> => {
    return apiFetch(`${base}/revisions/${postId}`);
  }, [base, postId]);

  const getRevision = useCallback(async (revisionId: number): Promise<CbRevisionWithData> => {
    return apiFetch(`${base}/revisions/${postId}/${revisionId}`);
  }, [base, postId]);

  const createRevision = useCallback(async (
    data: PageData,
    label = '',
    isSnapshot = false,
  ): Promise<{ id?: number; skipped?: boolean; created?: boolean }> => {
    return apiFetch(`${base}/revisions/${postId}`, {
      method: 'POST',
      body: JSON.stringify({ data, label, is_snapshot: isSnapshot }),
    });
  }, [base, postId]);

  const restoreRevision = useCallback(async (
    revisionId: number,
  ): Promise<{ restored: boolean; data: PageData }> => {
    return apiFetch(`${base}/revisions/${postId}/${revisionId}/restore`, {
      method: 'POST',
      body: '{}',
    });
  }, [base, postId]);

  const deleteRevision = useCallback(async (revisionId: number): Promise<{ deleted: boolean }> => {
    return apiFetch(`${base}/revisions/${postId}/${revisionId}`, { method: 'DELETE' });
  }, [base, postId]);

  // ── Class-Based Styling System ──────────────────────────────────────────
  const getClasses = useCallback(async (params?: { search?: string; favorite?: boolean }): Promise<JalpiClass[]> => {
    const qs = new URLSearchParams();
    if (params?.search) qs.set('search', params.search);
    if (params?.favorite) qs.set('favorite', '1');
    const query = qs.toString();
    return apiFetch(`${base}/classes${query ? '?' + query : ''}`);
  }, [base]);

  const createClass = useCallback(async (name: string, definition?: Partial<JalpiClass>): Promise<JalpiClass> => {
    return apiFetch(`${base}/classes`, { method: 'POST', body: JSON.stringify({ name, ...definition }) });
  }, [base]);

  const updateClass = useCallback(async (key: string, definition: Partial<JalpiClass>): Promise<JalpiClass> => {
    return apiFetch(`${base}/classes/${key}`, { method: 'PATCH', body: JSON.stringify(definition) });
  }, [base]);

  const duplicateClass = useCallback(async (key: string): Promise<JalpiClass> => {
    return apiFetch(`${base}/classes/${key}/duplicate`, { method: 'POST', body: '{}' });
  }, [base]);

  const deleteClass = useCallback(async (key: string): Promise<{ deleted: boolean }> => {
    return apiFetch(`${base}/classes/${key}`, { method: 'DELETE' });
  }, [base]);

  const toggleFavoriteClass = useCallback(async (key: string): Promise<JalpiClass> => {
    return apiFetch(`${base}/classes/${key}/favorite`, { method: 'POST', body: '{}' });
  }, [base]);

  const getClassUsage = useCallback(async (key: string): Promise<{ count: number }> => {
    return apiFetch(`${base}/classes/${key}/usage`);
  }, [base]);

  const reorderClasses = useCallback(async (keys: string[]): Promise<{ success: boolean }> => {
    return apiFetch(`${base}/classes/reorder`, { method: 'POST', body: JSON.stringify({ keys }) });
  }, [base]);

  const exportClasses = useCallback(async (): Promise<{ version: string; classes: JalpiClass[] }> => {
    return apiFetch(`${base}/classes/export`);
  }, [base]);

  const importClasses = useCallback(async (
    classes: JalpiClass[],
    conflictStrategy: 'skip' | 'overwrite' | 'duplicate' = 'skip',
  ): Promise<{ created: number; updated: number; skipped: number }> => {
    return apiFetch(`${base}/classes/import`, {
      method: 'POST',
      body: JSON.stringify({ classes, conflict_strategy: conflictStrategy }),
    });
  }, [base]);

  // Uses the WordPress core REST API to search site pages
  const searchPages = useCallback(async (q: string): Promise<Array<{ id: number; title: string; link: string }>> => {
    const wpBase = base.replace(/\/jalpi-page-builder\/v1\/?$/, '');
    const raw = await apiFetch<Array<{ id: number; title: { rendered: string }; link: string }>>(
      `${wpBase}/wp/v2/pages?search=${encodeURIComponent(q)}&per_page=8&_fields=id,title,link`,
    );
    return raw.map(p => ({ id: p.id, title: p.title.rendered.replace(/<[^>]*>/g, ''), link: p.link }));
  }, [base]);

  return {
    loadPage, savePage, publishPage, loadDesignSystem, saveDesignSystem, getMedia, uploadMedia,
    getTemplates, getTemplate, createTemplate, deleteTemplate,
    getTemplateCategories, getTemplateUserLists, toggleTemplateFavorite, pushTemplateRecent,
    duplicateTemplate, exportTemplate, importTemplate,
    seedBundledTemplates,
    listRevisions, getRevision, createRevision, restoreRevision, deleteRevision,
    searchPages,
    getClasses, createClass, updateClass, duplicateClass, deleteClass,
    toggleFavoriteClass, getClassUsage, reorderClasses, exportClasses, importClasses,
  };
}
