/**
 * SocioWire frontend API client
 */

import { loadAuth } from "../auth/authStorage";

/**
 * Convert image URL to small variant (160x160)
 * Asset URLs: ..._l.jpg -> ..._s.webp, ..._m.webp -> ..._s.webp
 */
export function toSmallImageUrl(url) {
  if (!url || typeof url !== "string") return url;
  if (url.includes("_l.jpg") || url.includes("_l.jpeg")) {
    return url.replace(/_l\.jpe?g/i, "_s.webp");
  }
  if (url.includes("_m.webp")) {
    return url.replace("_m.webp", "_s.webp");
  }
  return url;
}

function apiBase() {
  const raw =
    (import.meta?.env?.VITE_API_BASE_URL || "")
      .toString()
      .replace(/\/+$/, "");
  if (raw) return raw + "/api";
  try {
    if (typeof window !== "undefined") {
      const host = window.location.hostname || "";
      if (host.startsWith("www.")) {
        return `https://${host.replace(/^www\./, "")}/api`;
      }
    }
  } catch {}
  return "/api";
}

function authHeaders() {
  try {
    const auth = loadAuth();
    const token = auth?.access_token;
    if (!token) return {};
    return { Authorization: `Bearer ${token}` };
  } catch {
    return {};
  }
}

const responseCache = new Map();

function cacheKeyFor(url, headers = {}) {
  const auth = headers?.Authorization || headers?.authorization;
  if (auth) return ""; // avoid caching authenticated responses
  return `GET:${url}`;
}

// Invalidate cache entries matching a pattern
function invalidateCacheMatching(pattern) {
  for (const key of responseCache.keys()) {
    if (key.includes(pattern)) {
      responseCache.delete(key);
    }
  }
}

function cloneJson(value) {
  try {
    return JSON.parse(JSON.stringify(value));
  } catch {
    return value;
  }
}

async function fetchJsonCached(url, { ttlMs = 0, headers = {}, credentials = "include", signal } = {}) {
  const key = cacheKeyFor(url, headers);
  if (ttlMs > 0 && key) {
    const hit = responseCache.get(key);
    if (hit && hit.expiresAt > Date.now()) {
      return cloneJson(hit.value);
    }
  }

  const res = await fetch(url, { credentials, headers, signal });
  if (!res.ok) {
    const text = await res.text().catch(() => "");
    const err = new Error(text || `HTTP ${res.status}`);
    err.status = res.status;
    throw err;
  }
  const data = await res.json().catch(() => null);
  if (ttlMs > 0 && key) {
    responseCache.set(key, { value: data, expiresAt: Date.now() + ttlMs });
  }
  return cloneJson(data);
}


function isAssetRef(value) {
  return typeof value === "string" && value.startsWith("asset://");
}

async function resolveAssetURLs(ids) {
  if (!ids || !ids.length) return {};
  const qs = new URLSearchParams();
  qs.set("ids", ids.join(","));
  try {
    const res = await fetch(`${apiBase()}/assets/resolve?${qs.toString()}`, {
      credentials: "include",
      headers: { ...authHeaders() },
    });
    if (!res.ok) return {};
    const data = await res.json();
    return (data && data.urls) || {};
  } catch (err) {
    console.warn("resolveAssetURLs error:", err);
    return {};
  }
}

async function resolveAssetRef(value) {
  if (!isAssetRef(value)) return value;
  const id = value.replace("asset://", "");
  const resolved = await resolveAssetURLs([id]);
  return resolved[id] || value;
}

export { resolveAssetRef };

async function resolveAssetRefsInPosts(posts) {
  if (!Array.isArray(posts) || posts.length === 0) return posts;
  const ids = [];
  for (const p of posts) {
    [p?.image_small, p?.image_medium, p?.image_large, p?.image, p?.media_url, p?.video_url].forEach((v) => {
      if (isAssetRef(v)) ids.push(v.replace("asset://", ""));
    });
    if (Array.isArray(p?.media_urls)) {
      p.media_urls.forEach((v) => {
        if (isAssetRef(v)) ids.push(v.replace("asset://", ""));
      });
    }
  }
  const uniq = Array.from(new Set(ids));
  if (!uniq.length) return posts;
  const resolved = await resolveAssetURLs(uniq);
  return posts.map((p) => {
    const clone = { ...p };
    if (clone.post_id && Number(clone.post_id) > 0) {
      if (!clone.id || Number(clone.id) !== Number(clone.post_id)) {
        clone.reco_id = clone.id;
        clone.id = clone.post_id;
      }
    }
    ["image_small", "image_medium", "image_large", "image", "media_url", "video_url"].forEach((k) => {
      const v = clone[k];
      if (isAssetRef(v)) {
        const id = v.replace("asset://", "");
        if (resolved[id]) clone[k] = resolved[id];
      }
    });
    if (Array.isArray(clone.media_urls)) {
      clone.media_urls = clone.media_urls.map((value) => {
        if (isAssetRef(value)) {
          const id = value.replace("asset://", "");
          return resolved[id] || value;
        }
        return value;
      });
    }
    return clone;
  });
}

async function resolveAssetRefsInIslands(islands) {
  if (!Array.isArray(islands) || islands.length === 0) return islands;
  const ids = [];
  for (const i of islands) {
    if (isAssetRef(i?.avatar_url)) ids.push(i.avatar_url.replace("asset://", ""));
  }
  const uniq = Array.from(new Set(ids));
  if (!uniq.length) return islands;
  const resolved = await resolveAssetURLs(uniq);
  return islands.map((i) => {
    if (isAssetRef(i?.avatar_url)) {
      const id = i.avatar_url.replace("asset://", "");
      if (resolved[id]) return { ...i, avatar_url: resolved[id] };
    }
    return i;
  });
}

/**
 * Récupère la liste des posts, avec filtres optionnels.
 */
export async function fetchPosts(filters = {}) {
  const params = new URLSearchParams();

  if (filters.category) params.set("category", filters.category);
  if (filters.subCategory) params.set("sub_category", filters.subCategory);
  if (filters.time) params.set("time", filters.time);
  if (filters.username) params.set("username", filters.username);

  // Validate and set lat/lon with proper bounds checking
  if (typeof filters.lat === "number") {
    if (filters.lat >= -90 && filters.lat <= 90) {
      params.set("lat", String(filters.lat));
    } else {
      console.warn("Invalid latitude value (must be -90 to 90):", filters.lat);
    }
  }
  if (typeof filters.lon === "number") {
    if (filters.lon >= -180 && filters.lon <= 180) {
      params.set("lon", String(filters.lon));
    } else {
      console.warn("Invalid longitude value (must be -180 to 180):", filters.lon);
    }
  }
  if (typeof filters.radiusKm === "number") {
    if (filters.radiusKm > 0 && filters.radiusKm <= 20000) {
      params.set("radius_km", String(filters.radiusKm));
    } else {
      console.warn("Invalid radius value (must be > 0 and <= 20000 km):", filters.radiusKm);
    }
  }
  if (typeof filters.limit === "number" && filters.limit > 0) {
    params.set("limit", String(filters.limit));
  }

  const qs = params.toString();
  const url = qs ? `${apiBase()}/posts?${qs}` : `${apiBase()}/posts`;

  try {
    const data = await fetchJsonCached(url, { ttlMs: 15000 });
    const items = Array.isArray(data) ? data : data?.items || [];
    return await resolveAssetRefsInPosts(items);
  } catch (err) {
    console.warn("fetchPosts failed (treated as empty)", err?.status || err);
    return [];
  }
}

/**
 * Récupère la position approx de l'utilisateur depuis le backend.
 */
export async function fetchIpLocation() {
  try {
    const data = await fetchJsonCached(`${apiBase()}/ip-location`, { ttlMs: 10 * 60 * 1000 });
    if (!data || data === "null") return null;

    const lat = data.lat;
    const lon = data.lon;

    if (
      typeof lat === "number" &&
      typeof lon === "number" &&
      !Number.isNaN(lat) &&
      !Number.isNaN(lon)
    ) {
      return [lon, lat]; // [lng, lat]
    }

    console.warn("fetchIpLocation: invalid payload", data);
    return null;
  } catch (err) {
    console.warn("fetchIpLocation: network error", err);
    return null;
  }
}

/**
 * Crée un nouveau post
 * token: access token Keycloak (string) ou null
 */
export async function createPost(payload, token) {
  const headers = {
    "Content-Type": "application/json",
    ...authHeaders(), // Always include auth headers from stored token
  };
  if (token) {
    headers["Authorization"] = `Bearer ${token}`; // Override with explicit token if provided
  }

  const res = await fetch(`${apiBase()}/post`, {
    method: "POST",
    headers,
    credentials: "include",
    body: JSON.stringify(payload),
  });

  if (!res.ok) {
    const text = await res.text().catch(() => "");
    let json = null;
    try { json = text ? JSON.parse(text) : null; } catch {}

    const errMsg =
      (json && (json.message || json.error_description || json.error)) ||
      (text && text.slice(0, 300)) ||
      `HTTP ${res.status}`;

    const err = new Error(errMsg);
    err.status = res.status;
    err.body = text;

    // Common backend shapes
    err.code =
      (json && (json.code || json.error)) ||
      (res.status === 403 && /email[_\s-]*not[_\s-]*verified/i.test(text || "")
        ? "email_not_verified"
        : "") ||
      "";

    throw err;
  }

  return res.json().catch(() => ({}));
}

/**
 * Fetch link preview data for a URL (auth required).
 */
export async function fetchPostPreview(url, token) {
  const raw = (url || "").toString().trim();
  if (!raw) return null;
  const headers = { "Content-Type": "application/json" };
  if (token) headers["Authorization"] = `Bearer ${token}`;
  else Object.assign(headers, authHeaders());

  const res = await fetch(`${apiBase()}/post/preview`, {
    method: "POST",
    headers,
    credentials: "include",
    body: JSON.stringify({ url: raw }),
  });

  if (!res.ok) {
    const text = await res.text().catch(() => "");
    const err = new Error(text || `HTTP ${res.status}`);
    err.status = res.status;
    throw err;
  }

  return res.json().catch(() => null);
}

/**
 * Récupère un post par ID (détails + counts)
 */
export async function fetchPostById(postId) {
  if (!postId) return null;
  const qs = new URLSearchParams();
  qs.set("id", String(postId));
  try {
    const data = await fetchJsonCached(`${apiBase()}/post?${qs.toString()}`, { ttlMs: 3000 });
    if (!data || typeof data !== "object") return null;
    const resolved = await resolveAssetRefsInPosts([data]);
    return resolved && resolved[0] ? resolved[0] : data;
  } catch {
    return null;
  }
}

export async function fetchPostByUrl(url) {
  const raw = (url || "").toString().trim();
  if (!raw) return null;
  const qs = new URLSearchParams();
  qs.set("url", raw);
  try {
    const data = await fetchJsonCached(`${apiBase()}/post?${qs.toString()}`, { ttlMs: 15000 });
    if (!data || typeof data !== "object") return null;
    const resolved = await resolveAssetRefsInPosts([data]);
    return resolved && resolved[0] ? resolved[0] : data;
  } catch {
    return null;
  }
}

export async function fetchPostTruth(postId) {
  if (!postId) return null;
  const qs = new URLSearchParams();
  qs.set("post_id", String(postId));
  try {
    const res = await fetch(`${apiBase()}/post/truth?${qs.toString()}`, {
      credentials: "include",
      headers: { ...authHeaders() },
    });
    if (!res.ok) return null;
    const data = await res.json().catch(() => null);
    if (!data) return null;
    // Normalize response - API returns { truth: {...} }
    const truth = data?.truth || data || {};

    // truth_score is the main combined score (0-100)
    const truthScore = truth.truth_score ?? null;
    const aiScore = truth.ai_score ?? null;
    const voteCount = truth.total_votes ?? truth.vote_count ?? 0;

    // Return null only if we have no data at all
    if (truthScore === null && aiScore === null) return null;

    return {
      truth_score: truthScore ?? 50, // Main display score
      ai_score: aiScore,
      user_score: truthScore, // For backward compatibility
      total_votes: voteCount,
      vote_count: voteCount,
      user_vote: truth.user_vote ?? null,
    };
  } catch {
    return null;
  }
}

export async function votePostTruth(postId, vote) {
  if (!postId) throw new Error("post_id required");
  const body = JSON.stringify({ post_id: postId, vote });
  const res = await fetch(`${apiBase()}/post/truth-vote`, {
    method: "POST",
    headers: { "Content-Type": "application/json", ...authHeaders() },
    credentials: "include",
    body,
  });
  if (!res.ok) {
    const text = await res.text().catch(() => "");
    throw new Error(text || "truth vote failed");
  }
  const data = await res.json().catch(() => ({}));
  // Normalize response - API returns { ok: true, truth: {...} }
  const truth = data?.truth || data || {};
  const voteCount = truth.vote_count ?? truth.total_votes ?? truth.count ?? 0;
  const aiScore = truth.ai_score ?? truth.ai ?? 50;
  const truthScore = truth.truth_score ?? truth.TruthScore ?? 50; // Final calculated score
  const userVoteEffect = truth.user_score ?? 0; // Vote effect (+/- points), not the score
  return {
    ai_score: aiScore,
    user_score: userVoteEffect,
    truth_score: truthScore, // Main display score - the final calculated value
    total_votes: voteCount,
    vote_count: voteCount,
    user_vote: vote, // The vote that was just cast
  };
}

export async function updatePostVisibility(postId, payload = {}, token) {
  if (!postId) throw new Error("post_id required");
  const headers = { "Content-Type": "application/json" };
  if (token) headers["Authorization"] = `Bearer ${token}`;
  const body = JSON.stringify({ post_id: postId, ...payload });
    const res = await fetch(`${apiBase()}/post/visibility`, {
    method: "PATCH",
    headers,
    credentials: "include",
    body,
  });
  if (!res.ok) {
    const text = await res.text().catch(() => "");
    throw new Error(text || `HTTP ${res.status}`);
  }
  return res.json().catch(() => ({}));
}



/**
 * Crée un nouvel utilisateur via le backend
 * (backend: POST /api/signup)
 * payload: { username, email, password, firstName, lastName }
 */
export async function registerUser(payload) {
    const res = await fetch(`${apiBase()}/signup`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    credentials: "include",
    body: JSON.stringify(payload),
  });

  if (!res.ok) {
    const text = await res.text().catch(() => "");
    console.error("registerUser failed", res.status, text);
    throw new Error(text || "Failed to register user");
  }

  return res.json().catch(() => ({}));
}

/**
 * NEW: Fetch intelligent personalized feed with screen-aware prioritization
 * Uses backend /api/smart-feed endpoint (which proxies to reco-service)
 */
export async function fetchSmartFeed(params = {}, { signal } = {}) {
  const qs = new URLSearchParams();

  if (params.user_id) qs.set("user_id", String(params.user_id));
  if (params.username) qs.set("username", params.username);
  if (typeof params.lat === "number") qs.set("lat", String(params.lat));
  if (typeof params.lon === "number") qs.set("lon", String(params.lon));
  if (typeof params.radius_km === "number") qs.set("radius_km", String(params.radius_km));
  if (typeof params.limit === "number") qs.set("limit", String(params.limit));
  if (typeof params.zoom === "number") qs.set("zoom", String(params.zoom));
  if (params.category) qs.set("category", String(params.category));
  if (params.sub_category) qs.set("sub_category", String(params.sub_category));
  if (params.time_hours) qs.set("time_hours", String(params.time_hours));
  if (params.content_type) qs.set("content_type", String(params.content_type));

  // NEW: Screen bounds for on-screen prioritization
  if (params.bounds) {
    const { minLat, minLon, maxLat, maxLon } = params.bounds;
    if (minLat != null) qs.set("minLat", String(minLat));
    if (minLon != null) qs.set("minLon", String(minLon));
    if (maxLat != null) qs.set("maxLat", String(maxLat));
    if (maxLon != null) qs.set("maxLon", String(maxLon));
  }

  const url = `${apiBase()}/smart-feed?${qs.toString()}`;

  try {
    const data = await fetchJsonCached(url, { ttlMs: 10000, signal });
    const items = Array.isArray(data) ? data : data.results || data.items || [];
    return await resolveAssetRefsInPosts(items);
  } catch (err) {
    if (err.name === "AbortError") return []; // silently ignore aborted requests
    console.warn("fetchSmartFeed error:", err);
    return [];
  }
}

/**
 * NEW: Unified search across posts, profiles, and places
 * Uses backend /api/unified-search endpoint (which proxies to reco-service)
 */
export async function fetchUnifiedSearch(query, params = {}) {
  const qs = new URLSearchParams();

  qs.set("q", query);
  if (params.type) qs.set("type", params.type); // all|posts|profiles|places
  if (typeof params.lat === "number") qs.set("lat", String(params.lat));
  if (typeof params.lon === "number") qs.set("lon", String(params.lon));
  if (typeof params.radius_km === "number") qs.set("radius_km", String(params.radius_km));
  if (typeof params.limit === "number") qs.set("limit", String(params.limit));

  const url = `${apiBase()}/unified-search?${qs.toString()}`;

  try {
    const data = await fetchJsonCached(url, { ttlMs: 15000 });
    if (data && typeof data === "object" && !Array.isArray(data)) {
      if (Array.isArray(data.posts)) {
        data.posts = await resolveAssetRefsInPosts(data.posts);
      }
      return data;
    }
    return { posts: [], profiles: [], places: [] };
  } catch (err) {
    console.warn("fetchUnifiedSearch error:", err);
    return { posts: [], profiles: [], places: [] };
  }
}

/**
 * NEW: Poll for new posts (socket.io compatible)
 * Uses reco-service /api/poll endpoint
 */
export async function pollNewPosts(params = {}) {
  const qs = new URLSearchParams();

  if (typeof params.since_id === "number") qs.set("since_id", String(params.since_id));
  if (typeof params.lat === "number") qs.set("lat", String(params.lat));
  if (typeof params.lon === "number") qs.set("lon", String(params.lon));
  if (typeof params.radius_km === "number") qs.set("radius_km", String(params.radius_km));
  if (params.category) qs.set("category", params.category);
  if (typeof params.limit === "number") qs.set("limit", String(params.limit));

  const url = `${apiBase()}/poll?${qs.toString()}`;

  try {
    const res = await fetch(url, { credentials: "include" });
    if (!res.ok) {
      console.warn("pollNewPosts failed", res.status);
      return { posts: [], count: 0, max_id: 0 };
    }
    const data = await res.json();
    if (data && Array.isArray(data.posts)) {
      data.posts = await resolveAssetRefsInPosts(data.posts);
    }
    return data;
  } catch (err) {
    console.warn("pollNewPosts error:", err);
    return { posts: [], count: 0, max_id: 0 };
  }
}

/**
 * ISLAND SERVICE: Fetch islands in viewport
 * Queries island-service microservice (Go) for user islands
 *
 * NOTE: Currently using MOCK DATA for testing
 * Replace with real API call when island-service is ready
 */
export async function fetchIslands(params = {}) {
  const qs = new URLSearchParams();

  if (typeof params.lat === "number") qs.set("lat", String(params.lat));
  if (typeof params.lon === "number") qs.set("lon", String(params.lon));
  if (typeof params.radius_km === "number") qs.set("radius_km", String(params.radius_km));
  if (typeof params.limit === "number") qs.set("limit", String(params.limit));

  const url = `${apiBase()}/islands/nearby?${qs.toString()}`;

  try {
    const res = await fetch(url, { credentials: "include" });
    if (!res.ok) {
      console.warn("fetchIslands failed", res.status);
      return [];
    }
    const data = await res.json();
    const items = Array.isArray(data) ? data : data.islands || [];
    return await resolveAssetRefsInIslands(items);
  } catch (err) {
    console.warn("fetchIslands error:", err);
    return [];
  }
}

/**
 * ISLAND SERVICE: Fetch single island details
 * Gets full island data including content (posts, apps)
 *
 * NOTE: Currently using MOCK DATA for testing
 */
export async function fetchIsland(islandId) {
  if (!islandId) return null;
  const url = `${apiBase()}/islands/${encodeURIComponent(islandId)}`;

  try {
    const res = await fetch(url, { credentials: "include" });
    if (!res.ok) {
      console.warn("fetchIsland failed", res.status);
      return null;
    }
    const data = await res.json();
    if (data && data.avatar_url && isAssetRef(data.avatar_url)) {
      const resolved = await resolveAssetRefsInIslands([data]);
      return resolved[0] || data;
    }
    return data;
  } catch (err) {
    console.warn("fetchIsland error:", err);
    return null;
  }
}

export async function fetchIslandByUsername(username) {
  if (!username) return null;
  const url = `${apiBase()}/islands/by-username?username=${encodeURIComponent(username)}`;
  try {
    const res = await fetch(url, { credentials: "include" });
    if (!res.ok) {
      console.warn("fetchIslandByUsername failed", res.status);
      return null;
    }
    const data = await res.json();
    if (data && data.avatar_url && isAssetRef(data.avatar_url)) {
      const resolved = await resolveAssetRefsInIslands([data]);
      return resolved[0] || data;
    }
    return data;
  } catch (err) {
    console.warn("fetchIslandByUsername error:", err);
    return null;
  }
}

export async function uploadProfileAvatar(file, opts = {}) {
  if (!file) return { ok: false };
  const url = `${apiBase()}/profile/avatar`;
  const form = new FormData();
  form.append("file", file);
  if (opts.visibility) form.append("visibility", opts.visibility);
  if (opts.group_id) form.append("group_id", opts.group_id);
  if (opts.users && opts.users.length) form.append("users", opts.users.join(","));
  try {
    const res = await fetch(url, {
      method: "POST",
      body: form,
      credentials: "include",
      headers: { ...authHeaders() },
    });
    if (!res.ok) {
      const text = await res.text().catch(() => "");
      return { ok: false, status: res.status, text };
    }
    const out = await res.json();
    if (out && out.avatar_url) {
      out.avatar_url = await resolveAssetRef(out.avatar_url);
    }
    return out;
  } catch (err) {
    console.warn("uploadProfileAvatar error:", err);
    return { ok: false };
  }
}

export async function updateProfileUsername(username) {
  const url = `${apiBase()}/profile/username`;
  try {
    const res = await fetch(url, {
      method: "POST",
      credentials: "include",
      headers: { "Content-Type": "application/json", ...authHeaders() },
      body: JSON.stringify({ username }),
    });
    const text = await res.text().catch(() => "");
    let json = null;
    try {
      json = text ? JSON.parse(text) : null;
    } catch {
      json = null;
    }
    return { ok: res.ok, status: res.status, json, text };
  } catch (err) {
    console.warn("updateProfileUsername error:", err);
    return { ok: false, status: 0, json: null, text: err?.message || "error" };
  }
}

export async function fetchProfile(username = "") {
  const qs = new URLSearchParams();
  if (username) qs.set("username", username);
  const url = qs.toString() ? `${apiBase()}/profile?${qs}` : `${apiBase()}/profile`;
  try {
    const res = await fetch(url, {
      credentials: "include",
      headers: { ...authHeaders() },
    });
    if (!res.ok) {
      return null;
    }
    const data = await res.json();
    if (data && data.avatar_url) {
      data.avatar_url = await resolveAssetRef(data.avatar_url);
    }
    return data;
  } catch (err) {
    console.warn("fetchProfile error:", err);
    return null;
  }
}

export async function recordPostView(postId) {
  if (!postId) return { ok: false };
  try {
    const res = await fetch(`${apiBase()}/post/view`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      credentials: "include",
      body: JSON.stringify({ post_id: postId }),
    });
    const data = await res.json().catch(() => ({}));
    return { ok: res.ok, ...data };
  } catch {
    return { ok: false };
  }
}

export async function togglePostLike(postId, like = true) {
  if (!postId) return { ok: false };
  try {
    const res = await fetch(`${apiBase()}/post/like`, {
      method: "POST",
      headers: { "Content-Type": "application/json", ...authHeaders() },
      credentials: "include",
      body: JSON.stringify({ post_id: postId, like }),
    });
    const data = await res.json().catch(() => ({}));
    return { ok: res.ok, ...data };
  } catch {
    return { ok: false };
  }
}

export async function recordPostShare(postId) {
  if (!postId) return { ok: false };
  try {
    const res = await fetch(`${apiBase()}/post/share`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      credentials: "include",
      body: JSON.stringify({ post_id: postId }),
    });
    const data = await res.json().catch(() => ({}));
    return { ok: res.ok, ...data };
  } catch {
    return { ok: false };
  }
}

export async function fetchPostComments(postId, limit = 20) {
  if (!postId) return [];
  const qs = new URLSearchParams();
  qs.set("post_id", String(postId));
  if (limit) qs.set("limit", String(limit));
  try {
    const res = await fetch(`${apiBase()}/post/comments?${qs.toString()}`, {
      credentials: "include",
    });
    if (!res.ok) return [];
    const data = await res.json();
    return Array.isArray(data) ? data : [];
  } catch {
    return [];
  }
}

export async function createPostComment(postId, body) {
  if (!postId || !body) return { ok: false };
  try {
    const res = await fetch(`${apiBase()}/post/comment`, {
      method: "POST",
      headers: { "Content-Type": "application/json", ...authHeaders() },
      credentials: "include",
      body: JSON.stringify({ post_id: postId, body }),
    });
    const data = await res.json().catch(() => ({}));
    return { ...data, ok: res.ok };
  } catch {
    return { ok: false };
  }
}

export async function fetchPostLikeState(postId) {
  if (!postId) return { ok: false };
  const qs = new URLSearchParams();
  qs.set("post_id", String(postId));
  try {
    const res = await fetch(`${apiBase()}/post/like-state?${qs.toString()}`, {
      credentials: "include",
      headers: { ...authHeaders() },
    });
    const data = await res.json().catch(() => ({}));
    return { ok: res.ok, ...data };
  } catch {
    return { ok: false };
  }
}

// Edit post
export async function editPost(postId, payload, token) {
  if (!postId) return { ok: false };
  const qs = new URLSearchParams();
  qs.set("id", String(postId));
  try {
    const headers = { "Content-Type": "application/json", ...authHeaders() };
    if (token) headers["Authorization"] = `Bearer ${token}`;
    const res = await fetch(`${apiBase()}/post?${qs.toString()}`, {
      method: "PATCH",
      credentials: "include",
      headers,
      body: JSON.stringify(payload),
    });
    const data = await res.json().catch(() => ({}));
    return { ok: res.ok, ...data };
  } catch {
    return { ok: false };
  }
}

// Send debug log to server
function debugLog(msg) {
  try {
    const data = JSON.stringify({
      level: "info",
      msg: msg,
      ts: new Date().toISOString(),
      src: "delete"
    });
    const blob = new Blob([data], { type: "application/json" });
    navigator.sendBeacon?.(`${apiBase()}/debug-log`, blob);
  } catch {}
}

// Delete post
export async function deletePost(postId, token) {
  if (!postId) return { ok: false };
  const qs = new URLSearchParams();
  qs.set("id", String(postId));
  const url = `${apiBase()}/post?${qs.toString()}`;
  debugLog(`START postId=${postId} url=${url}`);
  const t0 = Date.now();
  try {
    const headers = { ...authHeaders() };
    if (token) headers["Authorization"] = `Bearer ${token}`;
    const res = await fetch(url, {
      method: "DELETE",
      credentials: "include",
      headers,
    });
    debugLog(`FETCH done ${Date.now() - t0}ms status=${res.status}`);
    const data = await res.json().catch(() => ({}));
    debugLog(`JSON done ${Date.now() - t0}ms ok=${res.ok}`);
    if (res.ok) {
      // Invalidate cache for this post
      invalidateCacheMatching(`id=${postId}`);
      invalidateCacheMatching(`post_id=${postId}`);
    }
    return { ok: res.ok, ...data };
  } catch (err) {
    debugLog(`ERROR ${Date.now() - t0}ms err=${err?.message || err}`);
    return { ok: false };
  }
}

// Friends API
export async function fetchFriends() {
  try {
    const res = await fetch(`${apiBase()}/friends`, {
      credentials: "include",
      headers: { ...authHeaders() },
    });
    const data = await res.json().catch(() => ({}));
    return { ok: res.ok, ...data };
  } catch {
    return { ok: false, friends: [] };
  }
}

export async function fetchFriendRequests() {
  try {
    const res = await fetch(`${apiBase()}/friends/requests`, {
      credentials: "include",
      headers: { ...authHeaders() },
    });
    const data = await res.json().catch(() => ({}));
    return { ok: res.ok, ...data };
  } catch {
    return { ok: false, requests: [] };
  }
}

// Emit event when friends list changes
function emitFriendsChanged() {
  try {
    window.dispatchEvent(new CustomEvent("sociowire:friends-changed"));
  } catch {}
}

export async function sendFriendRequest(username) {
  try {
    const res = await fetch(`${apiBase()}/friends/request`, {
      method: "POST",
      credentials: "include",
      headers: { "Content-Type": "application/json", ...authHeaders() },
      body: JSON.stringify({ username }),
    });
    const data = await res.json().catch(() => ({}));
    if (res.ok) emitFriendsChanged();
    return { ok: res.ok, ...data };
  } catch {
    return { ok: false };
  }
}

export async function acceptFriendRequest(username) {
  try {
    const res = await fetch(`${apiBase()}/friends/accept`, {
      method: "POST",
      credentials: "include",
      headers: { "Content-Type": "application/json", ...authHeaders() },
      body: JSON.stringify({ username }),
    });
    const data = await res.json().catch(() => ({}));
    if (res.ok) emitFriendsChanged();
    return { ok: res.ok, ...data };
  } catch {
    return { ok: false };
  }
}

export async function rejectFriendRequest(username) {
  try {
    const res = await fetch(`${apiBase()}/friends/reject`, {
      method: "POST",
      credentials: "include",
      headers: { "Content-Type": "application/json", ...authHeaders() },
      body: JSON.stringify({ username }),
    });
    const data = await res.json().catch(() => ({}));
    if (res.ok) emitFriendsChanged();
    return { ok: res.ok, ...data };
  } catch {
    return { ok: false };
  }
}

export async function cancelFriendRequest(username) {
  try {
    const res = await fetch(`${apiBase()}/friends/cancel`, {
      method: "POST",
      credentials: "include",
      headers: { "Content-Type": "application/json", ...authHeaders() },
      body: JSON.stringify({ username }),
    });
    const data = await res.json().catch(() => ({}));
    if (res.ok) emitFriendsChanged();
    return { ok: res.ok, ...data };
  } catch {
    return { ok: false };
  }
}

export async function removeFriend(username) {
  try {
    const res = await fetch(`${apiBase()}/friends/remove`, {
      method: "POST",
      credentials: "include",
      headers: { "Content-Type": "application/json", ...authHeaders() },
      body: JSON.stringify({ username }),
    });
    const data = await res.json().catch(() => ({}));
    if (res.ok) emitFriendsChanged();
    return { ok: res.ok, ...data };
  } catch {
    return { ok: false };
  }
}

export async function fetchFriendStatus(username) {
  if (!username) return { ok: false, status: "none" };
  const qs = new URLSearchParams();
  qs.set("username", username);
  try {
    const res = await fetch(`${apiBase()}/friends/status?${qs.toString()}`, {
      credentials: "include",
      headers: { ...authHeaders() },
    });
    const data = await res.json().catch(() => ({}));
    return { ok: res.ok, ...data };
  } catch {
    return { ok: false, status: "none" };
  }
}

export async function searchUsers(query) {
  if (!query || query.length < 2) return { ok: true, users: [] };
  const qs = new URLSearchParams();
  qs.set("q", query);
  try {
    const res = await fetch(`${apiBase()}/users/search?${qs.toString()}`, {
      credentials: "include",
    });
    const data = await res.json().catch(() => ({}));
    return { ok: res.ok, ...data };
  } catch {
    return { ok: false, users: [] };
  }
}

export async function updateTimezone(timezone) {
  try {
    const res = await fetch(`${apiBase()}/profile/timezone`, {
      method: "PATCH",
      credentials: "include",
      headers: { "Content-Type": "application/json", ...authHeaders() },
      body: JSON.stringify({ timezone }),
    });
    const data = await res.json().catch(() => ({}));
    return { ok: res.ok, ...data };
  } catch {
    return { ok: false };
  }
}

export function getBrowserTimezone() {
  try {
    return Intl.DateTimeFormat().resolvedOptions().timeZone || "";
  } catch {
    return "";
  }
}

// Weather API
export async function fetchWeatherByCity(city) {
  const url = `${apiBase()}/weather?city=${encodeURIComponent(city)}`;
  const res = await fetch(url);
  if (!res.ok) return null;
  return res.json();
}

export async function fetchWeatherByCoords(lat, lon) {
  const url = `${apiBase()}/weather?lat=${lat}&lon=${lon}`;
  const res = await fetch(url);
  if (!res.ok) return null;
  return res.json();
}

export async function fetchWeatherByPostId(postId) {
  const url = `${apiBase()}/weather?post_id=${postId}`;
  const res = await fetch(url);
  if (!res.ok) return null;
  return res.json();
}

export async function fetchWeatherPosts() {
  // Fetch all weather cities globally (lat=0,lon=0 with huge radius)
  const url = `${apiBase()}/posts?content_type=weather&lat=0&lon=0&radius_km=50000&limit=100`;
  const res = await fetch(url);
  if (!res.ok) return [];
  return res.json();
}

/**
 * Change password for the authenticated user
 * @param {string} oldPassword - Current password (optional but recommended)
 * @param {string} newPassword - New password (min 8 chars)
 */
export async function changePassword(oldPassword, newPassword) {
  const url = `${apiBase()}/change-password`;
  try {
    const res = await fetch(url, {
      method: "POST",
      credentials: "include",
      headers: { "Content-Type": "application/json", ...authHeaders() },
      body: JSON.stringify({
        old_password: oldPassword || "",
        new_password: newPassword,
      }),
    });
    const text = await res.text().catch(() => "");
    let json = null;
    try {
      json = text ? JSON.parse(text) : null;
    } catch {
      json = null;
    }
    return {
      ok: res.ok,
      status: res.status,
      json,
      error: json?.error || (res.ok ? null : text || "Password change failed"),
      message: json?.message || "",
    };
  } catch (err) {
    console.warn("changePassword error:", err);
    return { ok: false, status: 0, json: null, error: err?.message || "error" };
  }
}

/**
 * Upload media file (image or video) for posts
 * Returns { ok, url, thumbnail_url, error }
 */
export async function uploadPostMedia(file, opts = {}) {
  if (!file) return { ok: false, error: "No file provided" };

  // Use main API for uploads (asset-service proxy)
  const url = `${apiBase()}/assets/upload`;

  const form = new FormData();
  form.append("file", file);
  if (opts.type) form.append("type", opts.type); // "image" or "video"

  try {
    const res = await fetch(url, {
      method: "POST",
      body: form,
      credentials: "include",
      headers: { ...authHeaders() },
    });

    if (!res.ok) {
      const text = await res.text().catch(() => "");
      return { ok: false, status: res.status, error: text || `HTTP ${res.status}` };
    }

    const data = await res.json();
    return {
      ok: true,
      url: data.url || data.file_url || "",
      thumbnail_url: data.thumbnail_url || data.thumb_url || "",
      ...data,
    };
  } catch (err) {
    console.warn("uploadPostMedia error:", err);
    return { ok: false, error: err?.message || "Upload failed" };
  }
}

/**
 * Get user's current geolocation
 * Returns { ok, lat, lng, error }
 */
export function getCurrentLocation() {
  return new Promise((resolve) => {
    if (!navigator.geolocation) {
      resolve({ ok: false, error: "Geolocation not supported" });
      return;
    }
    navigator.geolocation.getCurrentPosition(
      (pos) => {
        resolve({
          ok: true,
          lat: pos.coords.latitude,
          lng: pos.coords.longitude,
          accuracy: pos.coords.accuracy,
        });
      },
      (err) => {
        resolve({ ok: false, error: err.message || "Location access denied" });
      },
      { enableHighAccuracy: true, timeout: 10000, maximumAge: 60000 }
    );
  });
}
