/**
 * Oracle API Client — production-only client for the Oracle v1 backend.
 */

import type {
  CanvasPage,
  PromptSubmitRequest,
  PromptSubmitResponse,
  ForkCreateRequest,
  ForkCreateResponse,
  MergeRequestCreateRequest,
  MergeRequest,
  MergeReviewRequest,
  ComponentTemplate,
  UserProfile,
  OracleWSMessage,
  OracleEnvelope,
  CanvasPageRevision,
} from '../types/canvas';
import { VELOCITY_TOKEN_KEY } from '@/lib/velocitySession';

function getBrowserOrigin(): string {
  return typeof window !== 'undefined' ? window.location.origin : '';
}

function resolveBaseUrl(): string {
  const configured = (import.meta.env.VITE_ORACLE_API_URL as string | undefined)?.trim();
  if (configured) {
    return configured.replace(/\/$/, '');
  }
  return getBrowserOrigin();
}

function resolveWsUrl(): string {
  const configured = (import.meta.env.VITE_ORACLE_WS_URL as string | undefined)?.trim();
  if (configured) {
    return configured.replace(/\/$/, '');
  }
  const origin = getBrowserOrigin();
  return origin ? origin.replace(/^http/, 'ws') : '';
}

const BASE_URL = resolveBaseUrl();
const WS_URL = resolveWsUrl();

function apiUrl(path: string): string {
  return `${BASE_URL}/api/oracle/v1${path}`;
}

async function apiFetch<T>(
  path: string,
  options?: RequestInit & { idempotencyKey?: string },
): Promise<T> {
  const headers: Record<string, string> = {
    'Content-Type': 'application/json',
    'X-Oracle-Contract-Version': 'v1',
    ...(options?.idempotencyKey ? { 'Idempotency-Key': options.idempotencyKey } : {}),
  };

  const token = localStorage.getItem('oracle_jwt') ?? localStorage.getItem(VELOCITY_TOKEN_KEY);
  if (token) headers.Authorization = `Bearer ${token}`;

  const res = await fetch(apiUrl(path), { ...options, headers });
  if (!res.ok) {
    const body = await res.json().catch(() => ({}));
    throw Object.assign(new Error(body?.error?.message ?? body?.detail?.errors?.join?.(', ') ?? `HTTP ${res.status}`), { apiError: body });
  }

  const body = await res.json() as OracleEnvelope<T> | T;
  if (typeof body === 'object' && body !== null && 'data' in body) {
    return (body as OracleEnvelope<T>).data;
  }
  return body as T;
}

export async function fetchMe(): Promise<UserProfile> {
  return apiFetch<UserProfile>('/me');
}

export async function fetchCanvasPage(pageId: string): Promise<CanvasPage> {
  return apiFetch<CanvasPage>(`/canvas-pages/${pageId}`);
}

export async function listCanvasPages(search?: string): Promise<CanvasPage[]> {
  const qs = new URLSearchParams();
  if (search?.trim()) qs.set('search', search.trim());
  return apiFetch<CanvasPage[]>(`/canvas-pages${qs.toString() ? `?${qs.toString()}` : ''}`);
}

export async function createCanvasPage(title = 'Untitled Canvas'): Promise<CanvasPage> {
  return apiFetch<CanvasPage>('/canvas-pages', {
    method: 'POST',
    body: JSON.stringify({ title }),
  });
}

export async function renameCanvasPage(pageId: string, title: string): Promise<CanvasPage> {
  return apiFetch<CanvasPage>(`/canvas-pages/${pageId}`, {
    method: 'PATCH',
    body: JSON.stringify({ title }),
  });
}

export async function deleteCanvasPage(pageId: string): Promise<{ pageId: string; deleted: boolean }> {
  return apiFetch<{ pageId: string; deleted: boolean }>(`/canvas-pages/${pageId}`, {
    method: 'DELETE',
  });
}

export async function submitPrompt(
  pageId: string,
  payload: PromptSubmitRequest,
): Promise<PromptSubmitResponse> {
  return apiFetch<PromptSubmitResponse>(`/canvas-pages/${pageId}/prompts`, {
    method: 'POST',
    body: JSON.stringify(payload),
    idempotencyKey: payload.clientRequestId,
  });
}

export async function rollbackPage(
  pageId: string,
  targetRevision: number,
  clientRequestId: string,
): Promise<{ headRevision: number; pageId: string; components: CanvasPage['components'] }> {
  return apiFetch<{ headRevision: number; pageId: string; components: CanvasPage['components'] }>(
    `/canvas-pages/${pageId}/rollback`,
    {
      method: 'POST',
      body: JSON.stringify({ targetRevision, clientRequestId }),
      idempotencyKey: clientRequestId,
    },
  );
}

export async function listRevisions(pageId: string): Promise<CanvasPageRevision[]> {
  return apiFetch<CanvasPageRevision[]>(`/canvas-pages/${pageId}/revisions`);
}

export async function createFork(
  pageId: string,
  payload: ForkCreateRequest,
): Promise<ForkCreateResponse> {
  return apiFetch<ForkCreateResponse>(`/canvas-pages/${pageId}/forks`, {
    method: 'POST',
    body: JSON.stringify(payload),
  });
}

export async function openMergeRequest(
  payload: MergeRequestCreateRequest,
): Promise<MergeRequest> {
  return apiFetch<MergeRequest>('/merge-requests', {
    method: 'POST',
    body: JSON.stringify(payload),
  });
}

export async function reviewMergeRequest(
  mrId: string,
  payload: MergeReviewRequest,
): Promise<MergeRequest> {
  return apiFetch<MergeRequest>(`/merge-requests/${mrId}/review`, {
    method: 'POST',
    body: JSON.stringify(payload),
  });
}

export async function listMergeRequests(pageId: string): Promise<MergeRequest[]> {
  return apiFetch<MergeRequest[]>(`/merge-requests?targetPageId=${pageId}`);
}

export async function listComponentTemplates(filters?: {
  category?: string;
  status?: string;
}): Promise<ComponentTemplate[]> {
  const qs = new URLSearchParams(filters as Record<string, string>).toString();
  return apiFetch<ComponentTemplate[]>(`/component-templates${qs ? `?${qs}` : ''}`);
}

export async function synthesizeTemplate(params: {
  prompt: string;
  dataShape: string[];
  styleSignatureRef?: string;
}): Promise<ComponentTemplate> {
  return apiFetch<ComponentTemplate>('/component-templates/synthesize', {
    method: 'POST',
    body: JSON.stringify(params),
  });
}

export function connectPageSocket(
  pageId: string,
  handlers: {
    onMessage: (msg: OracleWSMessage) => void;
    onReconnect?: () => void;
    onOpen?: () => void;
    onClose: () => void;
  },
): () => void {
  if (!WS_URL && !BASE_URL) {
    handlers.onClose();
    return () => undefined;
  }

  const wsBase = WS_URL || BASE_URL.replace(/^http/, 'ws');
  let ws: WebSocket;
  let stopped = false;
  let retryTimeout: ReturnType<typeof setTimeout> | undefined;

  function connect() {
    ws = new WebSocket(`${wsBase}/ws/oracle/canvas/${pageId}`);

    ws.onopen = () => {
      handlers.onOpen?.();
    };

    ws.onmessage = (event) => {
      try {
        handlers.onMessage(JSON.parse(event.data as string) as OracleWSMessage);
      } catch {
        // Ignore malformed messages from the transport.
      }
    };

    ws.onclose = () => {
      handlers.onClose();
      if (!stopped) {
        retryTimeout = setTimeout(() => {
          handlers.onReconnect?.();
          connect();
        }, 3000);
      }
    };

    ws.onerror = () => {
      ws.close();
    };
  }

  connect();

  return () => {
    stopped = true;
    if (retryTimeout) clearTimeout(retryTimeout);
    ws?.close();
  };
}
