const API_BASE = process.env.NEXT_PUBLIC_API_URL || "";

const TOKEN_KEY = "tasteby_token";

export function getToken(): string | null {
  if (typeof window === "undefined") return null;
  return localStorage.getItem(TOKEN_KEY);
}

export function setToken(token: string) {
  localStorage.setItem(TOKEN_KEY, token);
}

export function removeToken() {
  localStorage.removeItem(TOKEN_KEY);
}

async function fetchApi<T>(path: string, init?: RequestInit): Promise<T> {
  const token = getToken();
  const headers: Record<string, string> = {
    "Content-Type": "application/json",
    ...((init?.headers as Record<string, string>) || {}),
  };
  if (token) {
    headers["Authorization"] = `Bearer ${token}`;
  }
  const res = await fetch(`${API_BASE}${path}`, {
    ...init,
    headers,
  });
  if (!res.ok) throw new Error(`API ${res.status}: ${res.statusText}`);
  return res.json();
}

export interface Restaurant {
  id: string;
  name: string;
  address: string | null;
  region: string | null;
  latitude: number;
  longitude: number;
  cuisine_type: string | null;
  price_range: string | null;
  google_place_id: string | null;
  tabling_url: string | null;
  catchtable_url: string | null;
  business_status: string | null;
  rating: number | null;
  rating_count: number | null;
  phone: string | null;
  website: string | null;
  channels?: string[];
  foods_mentioned?: string[];
  // #533 — 의미있는(strong/unknown) 영상 언급 수
  video_count?: number | null;
  // #322 LLM 검증
  hidden?: boolean;
  hidden_reason?: string | null;
  verified_at?: string | null;
}

export interface VideoLink {
  video_id: string;
  title: string;
  url: string;
  published_at: string | null;
  foods_mentioned: string[];
  evaluation: Record<string, string>;
  guests: string[];
  channel_name: string | null;
  channel_id: string | null;
}

export interface Channel {
  id: string;
  channel_id: string;
  channel_name: string;
  title_filter: string | null;
  description: string | null;
  tags: string | null;
  sort_order: number | null;
  video_count: number;
  last_scanned_at: string | null;
}

export interface Video {
  id: string;
  video_id: string;
  title: string;
  url: string;
  status: string;
  published_at: string | null;
  channel_name: string;
  has_transcript: boolean;
  has_llm: boolean;
  restaurant_count: number;
  matched_count: number;
}

export interface VideoRestaurant {
  restaurant_id: string;
  name: string;
  address: string | null;
  cuisine_type: string | null;
  price_range: string | null;
  region: string | null;
  foods_mentioned: string[];
  evaluation: Record<string, string>;
  guests: string[];
  google_place_id: string | null;
  has_location: boolean;
}

export interface VideoDetail extends Video {
  transcript: string | null;
  restaurants: VideoRestaurant[];
}

export interface User {
  id: string;
  email: string | null;
  nickname: string | null;
  avatar_url: string | null;
  is_admin?: boolean;
}

export interface Review {
  id: string;
  user_id: string;
  rating: number;
  review_text: string | null;
  visited_at: string | null;
  created_at: string;
  user_nickname: string | null;
  user_avatar_url: string | null;
}

export interface Memo {
  id: string;
  user_id: string;
  restaurant_id: string;
  rating: number | null;
  memo_text: string | null;
  visited_at: string | null;
  created_at: string;
  updated_at: string;
}

export interface DaemonConfig {
  scan_enabled: boolean;
  scan_interval_min: number;
  process_enabled: boolean;
  process_interval_min: number;
  process_limit: number;
  last_scan_at: string | null;
  last_process_at: string | null;
  updated_at: string | null;
}

export interface ReviewsResponse {
  reviews: Review[];
  avg_rating: number | null;
  review_count: number;
}

export const api = {
  getRestaurants(params?: {
    cuisine?: string;
    region?: string;
    channel?: string;
    limit?: number;
    offset?: number;
    min_mentions?: number;
  }) {
    const sp = new URLSearchParams();
    if (params?.cuisine) sp.set("cuisine", params.cuisine);
    if (params?.region) sp.set("region", params.region);
    if (params?.channel) sp.set("channel", params.channel);
    if (params?.limit) sp.set("limit", String(params.limit));
    if (params?.offset) sp.set("offset", String(params.offset));
    if (params?.min_mentions) sp.set("min_mentions", String(params.min_mentions));
    const qs = sp.toString();
    return fetchApi<Restaurant[]>(`/api/restaurants${qs ? `?${qs}` : ""}`);
  },

  getRestaurant(id: string) {
    return fetchApi<Restaurant>(`/api/restaurants/${id}`);
  },

  updateRestaurant(id: string, data: Partial<Restaurant>) {
    return fetchApi<{ ok: boolean }>(`/api/restaurants/${id}`, {
      method: "PUT",
      body: JSON.stringify(data),
    });
  },

  deleteRestaurant(id: string) {
    return fetchApi<{ ok: boolean }>(`/api/restaurants/${id}`, {
      method: "DELETE",
    });
  },

  getRestaurantVideos(id: string) {
    return fetchApi<VideoLink[]>(`/api/restaurants/${id}/videos`);
  },

  search(q: string, mode: "keyword" | "semantic" | "hybrid" = "keyword") {
    return fetchApi<Restaurant[]>(
      `/api/search?q=${encodeURIComponent(q)}&mode=${mode}`
    );
  },

  getChannels() {
    return fetchApi<Channel[]>("/api/channels");
  },

  loginWithGoogle(idToken: string) {
    return fetchApi<{ access_token: string; user: User }>("/api/auth/google", {
      method: "POST",
      body: JSON.stringify({ id_token: idToken }),
    });
  },

  getMe() {
    return fetchApi<User>("/api/auth/me");
  },

  getFavoriteStatus(restaurantId: string) {
    return fetchApi<{ favorited: boolean }>(`/api/restaurants/${restaurantId}/favorite`);
  },

  toggleFavorite(restaurantId: string) {
    return fetchApi<{ favorited: boolean }>(`/api/restaurants/${restaurantId}/favorite`, {
      method: "POST",
    });
  },

  getMyFavorites() {
    return fetchApi<Restaurant[]>("/api/users/me/favorites");
  },

  getReviews(restaurantId: string) {
    return fetchApi<ReviewsResponse>(`/api/restaurants/${restaurantId}/reviews`);
  },

  createReview(
    restaurantId: string,
    data: { rating: number; review_text?: string; visited_at?: string }
  ) {
    return fetchApi<Review>(`/api/restaurants/${restaurantId}/reviews`, {
      method: "POST",
      body: JSON.stringify(data),
    });
  },

  updateReview(
    reviewId: string,
    data: { rating: number; review_text?: string; visited_at?: string }
  ) {
    return fetchApi<Review>(`/api/reviews/${reviewId}`, {
      method: "PUT",
      body: JSON.stringify(data),
    });
  },

  deleteReview(reviewId: string) {
    return fetchApi<void>(`/api/reviews/${reviewId}`, {
      method: "DELETE",
    });
  },

  getMyReviews() {
    return fetchApi<(Review & { restaurant_id: string; restaurant_name: string | null })[]>(
      "/api/users/me/reviews?limit=100"
    );
  },

  // Memos
  getMemo(restaurantId: string) {
    return fetchApi<Memo>(`/api/restaurants/${restaurantId}/memo`);
  },

  upsertMemo(restaurantId: string, data: { rating?: number; memo_text?: string; visited_at?: string }) {
    return fetchApi<Memo>(`/api/restaurants/${restaurantId}/memo`, {
      method: "POST",
      body: JSON.stringify(data),
    });
  },

  deleteMemo(restaurantId: string) {
    return fetchApi<void>(`/api/restaurants/${restaurantId}/memo`, {
      method: "DELETE",
    });
  },

  getMyMemos() {
    return fetchApi<(Memo & { restaurant_name: string | null })[]>("/api/users/me/memos");
  },

  // Stats
  recordVisit() {
    return fetchApi<{ ok: boolean }>("/api/stats/visit", { method: "POST" });
  },

  getVisits() {
    return fetchApi<{ today: number; total: number }>("/api/stats/visits");
  },

  // Admin - Users
  getAdminUsers(params?: { limit?: number; offset?: number }) {
    const sp = new URLSearchParams();
    if (params?.limit) sp.set("limit", String(params.limit));
    if (params?.offset) sp.set("offset", String(params.offset));
    const qs = sp.toString();
    return fetchApi<{
      users: {
        id: string;
        email: string | null;
        nickname: string | null;
        avatar_url: string | null;
        is_admin: boolean;
        provider: string | null;
        created_at: string | null;
        favorite_count: number;
        review_count: number;
        memo_count: number;
      }[];
      total: number;
    }>(`/api/admin/users${qs ? `?${qs}` : ""}`);
  },

  updateAdminUserAdmin(userId: string, admin: boolean) {
    return fetchApi<{ success: boolean }>(`/api/admin/users/${userId}/admin`, {
      method: "PATCH",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ admin }),
    });
  },

  getAdminUserFavorites(userId: string) {
    return fetchApi<
      {
        id: string;
        name: string;
        address: string | null;
        region: string | null;
        cuisine_type: string | null;
        rating: number | null;
        business_status: string | null;
        created_at: string | null;
      }[]
    >(`/api/admin/users/${userId}/favorites`);
  },

  getAdminUserReviews(userId: string) {
    return fetchApi<
      {
        id: string;
        restaurant_id: string;
        rating: number;
        review_text: string | null;
        visited_at: string | null;
        created_at: string | null;
        restaurant_name: string | null;
      }[]
    >(`/api/admin/users/${userId}/reviews`);
  },

  getAdminUserMemos(userId: string) {
    return fetchApi<
      {
        id: string;
        restaurant_id: string;
        rating: number | null;
        memo_text: string | null;
        visited_at: string | null;
        created_at: string;
        restaurant_name: string | null;
      }[]
    >(`/api/admin/users/${userId}/memos`);
  },

  // Admin
  addChannel(channelId: string, channelName: string, titleFilter?: string) {
    return fetchApi<{ id: string; channel_id: string }>("/api/channels", {
      method: "POST",
      body: JSON.stringify({
        channel_id: channelId,
        channel_name: channelName,
        title_filter: titleFilter || null,
      }),
    });
  },

  searchTabling(restaurantId: string) {
    return fetchApi<{ title: string; url: string }[]>(
      `/api/restaurants/${restaurantId}/tabling-search`
    );
  },

  setTablingUrl(restaurantId: string, tablingUrl: string) {
    return fetchApi<{ ok: boolean }>(
      `/api/restaurants/${restaurantId}/tabling-url`,
      { method: "PUT", body: JSON.stringify({ tabling_url: tablingUrl }) }
    );
  },

  searchCatchtable(restaurantId: string) {
    return fetchApi<{ title: string; url: string }[]>(
      `/api/restaurants/${restaurantId}/catchtable-search`
    );
  },

  setCatchtableUrl(restaurantId: string, catchtableUrl: string) {
    return fetchApi<{ ok: boolean }>(
      `/api/restaurants/${restaurantId}/catchtable-url`,
      { method: "PUT", body: JSON.stringify({ catchtable_url: catchtableUrl }) }
    );
  },

  updateChannel(id: string, data: { description?: string; tags?: string; sort_order?: number }) {
    return fetchApi<{ ok: boolean }>(`/api/channels/${id}`, {
      method: "PUT",
      body: JSON.stringify(data),
    });
  },

  deleteChannel(channelId: string) {
    return fetchApi<{ ok: boolean }>(`/api/channels/${channelId}`, {
      method: "DELETE",
    });
  },

  scanChannel(channelId: string, full: boolean = false) {
    return fetchApi<{ total_fetched: number; new_videos: number }>(
      `/api/channels/${channelId}/scan${full ? "?full=true" : ""}`,
      { method: "POST" }
    );
  },

  getVideos(params?: { status?: string; limit?: number; offset?: number }) {
    const sp = new URLSearchParams();
    if (params?.status) sp.set("status", params.status);
    if (params?.limit) sp.set("limit", String(params.limit));
    if (params?.offset) sp.set("offset", String(params.offset));
    const qs = sp.toString();
    return fetchApi<Video[]>(`/api/videos${qs ? `?${qs}` : ""}`);
  },

  getVideoDetail(videoDbId: string) {
    return fetchApi<VideoDetail>(`/api/videos/${videoDbId}`);
  },

  skipVideo(videoDbId: string) {
    return fetchApi<{ ok: boolean }>(`/api/videos/${videoDbId}/skip`, {
      method: "POST",
    });
  },

  deleteVideo(videoDbId: string) {
    return fetchApi<{ ok: boolean }>(`/api/videos/${videoDbId}`, {
      method: "DELETE",
    });
  },

  getExtractPrompt() {
    return fetchApi<{ prompt: string }>("/api/videos/extract/prompt");
  },

  extractRestaurants(videoDbId: string, customPrompt?: string) {
    return fetchApi<{ ok: boolean; restaurants_extracted: number }>(
      `/api/videos/${videoDbId}/extract`,
      {
        method: "POST",
        body: JSON.stringify(customPrompt ? { prompt: customPrompt } : {}),
      }
    );
  },

  fetchTranscript(videoDbId: string, mode: "auto" | "manual" | "generated" = "auto") {
    return fetchApi<{ ok: boolean; length: number; source: string }>(
      `/api/videos/${videoDbId}/fetch-transcript?mode=${mode}`,
      { method: "POST" }
    );
  },

  uploadTranscript(videoDbId: string, text: string, source: string = "browser") {
    return fetchApi<{ ok: boolean; length: number; source: string }>(
      `/api/videos/${videoDbId}/upload-transcript`,
      { method: "POST", body: JSON.stringify({ text, source }) }
    );
  },

  triggerProcessing(limit: number = 5) {
    return fetchApi<{ restaurants_extracted: number }>(
      `/api/videos/process?limit=${limit}`,
      { method: "POST" }
    );
  },

  getBulkExtractPending() {
    return fetchApi<{ count: number; videos: { id: string; title: string }[] }>(
      "/api/videos/bulk-extract/pending"
    );
  },

  getBulkTranscriptPending() {
    return fetchApi<{ count: number; videos: { id: string; title: string }[] }>(
      "/api/videos/bulk-transcript/pending"
    );
  },

  updateVideo(videoDbId: string, data: { title: string }) {
    return fetchApi<{ ok: boolean }>(`/api/videos/${videoDbId}`, {
      method: "PUT",
      body: JSON.stringify(data),
    });
  },

  addManualRestaurant(
    videoDbId: string,
    data: {
      name: string;
      address?: string;
      region?: string;
      cuisine_type?: string;
      price_range?: string;
      foods_mentioned?: string[];
      evaluation?: string;
      guests?: string[];
    }
  ) {
    return fetchApi<{ ok: boolean; restaurant_id: string; link_id: string }>(
      `/api/videos/${videoDbId}/restaurants/manual`,
      { method: "POST", body: JSON.stringify(data) }
    );
  },

  deleteVideoRestaurant(videoDbId: string, restaurantId: string) {
    return fetchApi<{ ok: boolean }>(
      `/api/videos/${videoDbId}/restaurants/${restaurantId}`,
      { method: "DELETE" }
    );
  },

  updateVideoRestaurant(
    videoDbId: string,
    restaurantId: string,
    data: Partial<VideoRestaurant>
  ) {
    return fetchApi<{ ok: boolean }>(
      `/api/videos/${videoDbId}/restaurants/${restaurantId}`,
      { method: "PUT", body: JSON.stringify(data) }
    );
  },

  // Daemon config
  getDaemonConfig() {
    return fetchApi<DaemonConfig>("/api/daemon/config");
  },

  updateDaemonConfig(data: Partial<DaemonConfig>) {
    return fetchApi<{ ok: boolean }>("/api/daemon/config", {
      method: "PUT",
      body: JSON.stringify(data),
    });
  },

  runDaemonScan() {
    return fetchApi<{ ok: boolean; new_videos: number }>("/api/daemon/run/scan", {
      method: "POST",
    });
  },

  flushCache() {
    return fetchApi<{ ok: boolean }>("/api/admin/cache-flush", {
      method: "POST",
    });
  },

  runDaemonProcess(limit: number = 10) {
    return fetchApi<{ ok: boolean; restaurants_extracted: number }>(
      `/api/daemon/run/process?limit=${limit}`,
      { method: "POST" }
    );
  },

  // #322 — LLM 검증 어드민 API
  getVerifyPending() {
    return fetchApi<{ pending: number }>("/api/admin/restaurants/verify/pending");
  },
  verifyAll(batchSize: number = 10) {
    return fetchApi<{ processed: number }>(
      `/api/admin/restaurants/verify/all?batchSize=${batchSize}`,
      { method: "POST" }
    );
  },
  verifyOne(id: string) {
    return fetchApi<{ success: boolean; id: string }>(
      `/api/admin/restaurants/${id}/verify`,
      { method: "POST" }
    );
  },
  setRestaurantHidden(id: string, hidden: boolean, reason: string = "manual") {
    return fetchApi<{ success: boolean; id: string; hidden: boolean }>(
      `/api/admin/restaurants/${id}/hidden`,
      {
        method: "PATCH",
        body: JSON.stringify({ hidden, reason }),
      }
    );
  },
};
