import type { Recipe, RecipeDraft, Tag, ApiResponse, UrlImportResult, HarnessStatus } from '../types/recipe';

const API_BASE_URL = '/api';

async function request<T>(path: string, init?: RequestInit): Promise<T> {
  const response = await fetch(`${API_BASE_URL}${path}`, {
    headers: {
      'Content-Type': 'application/json',
      ...(init?.headers ?? {}),
    },
    ...init,
  });

  const result: ApiResponse<T> = await response.json();
  if (!response.ok || !result.success || result.data === null) {
    const message =
      typeof result?.error === 'string'
        ? result.error
        : `Request failed (${response.status} ${response.statusText})`;
    throw new Error(message);
  }

  return result.data;
}

export async function fetchRecipes(params?: {
  search?: string;
  offset?: number;
  limit?: number;
  tagId?: number | null;
  tagIds?: number[];
}): Promise<Recipe[]> {
  const url = new URL(`${API_BASE_URL}/recipes`, window.location.origin);

  if (params?.search) {
    url.searchParams.set('search', params.search);
  }
  if (params?.offset !== undefined) {
    url.searchParams.set('offset', params.offset.toString());
  }
  if (params?.limit !== undefined) {
    url.searchParams.set('limit', params.limit.toString());
  }

  const normalizedTagIds = (params?.tagIds ?? []).filter((id): id is number => Number.isInteger(id) && id > 0);
  if (normalizedTagIds.length > 0) {
    url.searchParams.set('tagIds', normalizedTagIds.join(','));
  } else if (params?.tagId !== undefined && params?.tagId !== null) {
    // Backward compatibility for older callers
    url.searchParams.set('tagId', params.tagId.toString());
  }

  const response = await fetch(url.toString());
  if (!response.ok) {
    throw new Error(`Failed to fetch recipes: ${response.statusText}`);
  }

  const result: ApiResponse<Recipe[]> = await response.json();
  if (!result.success || !result.data) {
    throw new Error(result.error || 'Failed to fetch recipes');
  }

  return result.data;
}

export async function fetchRecipe(id: number): Promise<Recipe> {
  return request<Recipe>(`/recipes/${id}`);
}

export async function createRecipe(recipe: RecipeDraft): Promise<Recipe> {
  const steps = recipe.instructions.map((instruction) => ({ instruction }));
  const payload = {
    ...recipe,
    steps,
  };
  return request<Recipe>('/recipes', {
    method: 'POST',
    body: JSON.stringify(payload),
  });
}

export async function updateRecipe(
  id: number,
  updates: Partial<Omit<Recipe, 'id' | 'created_at' | 'updated_at'>>
): Promise<Recipe> {
  const payload: Record<string, unknown> = { ...updates };

  if (Array.isArray(updates.instructions)) {
    payload.steps = updates.instructions.map((instruction) => ({ instruction }));
    delete payload.instructions;
  }

  return request<Recipe>(`/recipes/${id}`, {
    method: 'PUT',
    body: JSON.stringify(payload),
  });
}

export async function deleteRecipe(id: number): Promise<void> {
  await request<boolean>(`/recipes/${id}`, { method: 'DELETE' });
}

export async function fetchTags(): Promise<Tag[]> {
  return request<Tag[]>('/tags');
}

export async function createTag(tag: Omit<Tag, 'id'>): Promise<Tag> {
  return request<Tag>('/tags', {
    method: 'POST',
    body: JSON.stringify({ name: tag.name }),
  });
}

export async function fetchRecipeTags(recipeId: number): Promise<Tag[]> {
  const recipe = await fetchRecipe(recipeId);
  return recipe.tags ?? [];
}

export async function assignTagToRecipe(recipeId: number, tagId: number): Promise<void> {
  await request<boolean>(`/tags/${recipeId}/assign`, {
    method: 'POST',
    body: JSON.stringify({ tag_id: tagId }),
  });
}

export async function removeTagFromRecipe(recipeId: number, tagId: number): Promise<void> {
  await request<boolean>(`/tags/${recipeId}/remove`, {
    method: 'POST',
    body: JSON.stringify({ tag_id: tagId }),
  });
}

export async function importRecipeFromUrl(url: string): Promise<UrlImportResult> {
  return request<UrlImportResult>('/import/url', {
    method: 'POST',
    body: JSON.stringify({ url }),
  });
}

export async function fetchHarnessStatus(): Promise<HarnessStatus> {
  const data = await request<any>('/harness/status');

  return {
    running: true,
    version: data?.commit?.hash ?? 'unknown',
    uptime: 0,
    keepalive: data?.keepalive,
    commit: data?.commit,
    todo: data?.todo,
    workerHeartbeatHistory: data?.workerHeartbeatHistory,
  };
}
