// Thin fetch wrapper around the CompanyBrain API.
// Pick the backend URL based on where the front is being served from. Local
// development talks to the dotnet dev server; everything else hits the Railway
// deployment. An explicit override via window.API_BASE (set before this script
// loads) takes precedence — handy for previewing a feature branch.
(function pickApiBase() {
  if (window.API_BASE) return;
  const host = window.location.hostname;
  const isLocal = host === "localhost" || host === "127.0.0.1" || host === "";
  window.API_BASE = isLocal
    ? "http://localhost:5153"
    : "https://api-production-5514.up.railway.app";
})();
window.CURRENT_USER_KEY = "companyBrain.currentUserId";
window.AUTH_KEY = "companyBrain.auth";

// "Login as" — stored client-side until real auth lands. Empty = System.
window.getCurrentUserId = () => {
  try { return localStorage.getItem(window.CURRENT_USER_KEY) || null; }
  catch { return null; }
};
window.setCurrentUserId = (id) => {
  try {
    if (id) localStorage.setItem(window.CURRENT_USER_KEY, id);
    else    localStorage.removeItem(window.CURRENT_USER_KEY);
  } catch { /* ignore */ }
  window.dispatchEvent(new CustomEvent("current-user-changed"));
};

// Auth gate — backed by /api/auth/login. The session payload is the member
// returned by the backend so we can render their name + initials in-app and
// keep the X-Demo-User-Id header populated for downstream requests.
window.getAuth = () => {
  try {
    const raw = localStorage.getItem(window.AUTH_KEY);
    return raw ? JSON.parse(raw) : null;
  } catch { return null; }
};
window.isAuthenticated = () => {
  const a = window.getAuth();
  return !!(a && a.member && a.member.id);
};
window.setSession = (member) => {
  try {
    localStorage.setItem(window.AUTH_KEY, JSON.stringify({ member, loggedInAt: Date.now() }));
    if (member?.id) localStorage.setItem(window.CURRENT_USER_KEY, member.id);
  } catch { /* ignore */ }
  window.dispatchEvent(new CustomEvent("auth-changed"));
  window.dispatchEvent(new CustomEvent("current-user-changed"));
};
window.logout = () => {
  try {
    localStorage.removeItem(window.AUTH_KEY);
    localStorage.removeItem(window.CURRENT_USER_KEY);
  } catch { /* ignore */ }
  window.dispatchEvent(new CustomEvent("auth-changed"));
  window.dispatchEvent(new CustomEvent("current-user-changed"));
};

async function request(method, path, body) {
  const headers = {};
  if (body) headers["Content-Type"] = "application/json";
  const actor = window.getCurrentUserId();
  if (actor) headers["X-Demo-User-Id"] = actor;
  const res = await fetch(`${window.API_BASE}${path}`, {
    method,
    headers,
    body: body ? JSON.stringify(body) : undefined,
  });
  if (!res.ok) {
    let message = `${method} ${path} → HTTP ${res.status}`;
    try {
      const data = await res.json();
      if (data?.error) message = data.error;
    } catch { /* not json */ }
    const err = new Error(message);
    err.status = res.status;
    throw err;
  }
  if (res.status === 204) return null;
  const text = await res.text();
  return text ? JSON.parse(text) : null;
}

// Multipart variant for file uploads. Same auth header conventions; lets the browser
// set the multipart boundary by *not* setting Content-Type ourselves.
async function uploadMultipart(path, formData) {
  const headers = {};
  const actor = window.getCurrentUserId();
  if (actor) headers["X-Demo-User-Id"] = actor;
  const res = await fetch(`${window.API_BASE}${path}`, { method: "POST", headers, body: formData });
  if (!res.ok) {
    let message = `POST ${path} → HTTP ${res.status}`;
    try {
      const data = await res.json();
      if (data?.error) message = data.error;
    } catch { /* not json */ }
    const err = new Error(message);
    err.status = res.status;
    throw err;
  }
  const text = await res.text();
  return text ? JSON.parse(text) : null;
}

window.api = {
  clients: {
    list:      ()    => request("GET",  "/api/clients"),
    get:       (id)  => request("GET",  `/api/clients/${id}`),
    create:    (p)   => request("POST", "/api/clients", p),
    update:    (id, p) => request("PUT", `/api/clients/${id}`, p),
    archive:   (id)  => request("POST", `/api/clients/${id}/archive`),
    unarchive: (id)  => request("POST", `/api/clients/${id}/unarchive`),
    delete:    (id)  => request("DELETE", `/api/clients/${id}`),
  },
  invoices: {
    list:   (status)  => request("GET", "/api/invoices" + (status ? `?status=${status}` : "")),
    get:    (id)      => request("GET", `/api/invoices/${id}`),
    create: (p)       => request("POST", "/api/invoices", p),
    update: (id, p)   => request("PUT", `/api/invoices/${id}`, p),
    delete: (id)      => request("DELETE", `/api/invoices/${id}`),
  },
  teamMembers: {
    list:   ()        => request("GET", "/api/team/members"),
    get:    (id)      => request("GET", `/api/team/members/${id}`),
    create: (p)       => request("POST", "/api/team/members", p),
    update: (id, p)   => request("PUT", `/api/team/members/${id}`, p),
    delete: (id)      => request("DELETE", `/api/team/members/${id}`),
  },
  activity: {
    recent:  (limit = 5)             => request("GET", `/api/activity?limit=${limit}`),
    history: (page = 1, pageSize = 25) => request("GET", `/api/activity/history?page=${page}&pageSize=${pageSize}`),
  },
  auth: {
    login: (email, password) => request("POST", "/api/auth/login", { email, password }),
  },
  storage: {
    contents: (folderId)        => request("GET", `/api/storage/contents${folderId ? `?folderId=${folderId}` : ""}`),
    createFolder: (p)           => request("POST", "/api/storage/folders", p),
    updateFolder: (id, p)       => request("PUT", `/api/storage/folders/${id}`, p),
    deleteFolder: (id)          => request("DELETE", `/api/storage/folders/${id}`),
    uploadFile: (file, opts) => {
      const fd = new FormData();
      fd.append("file", file);
      if (opts?.folderId) fd.append("folderId", opts.folderId);
      fd.append("isPrivate", opts?.isPrivate ? "true" : "false");
      return uploadMultipart("/api/storage/files", fd);
    },
    updateFile: (id, p)         => request("PUT", `/api/storage/files/${id}`, p),
    deleteFile: (id)            => request("DELETE", `/api/storage/files/${id}`),
    // Synchronous URL helper for direct-download anchors. The browser will hit the
    // endpoint, send the demo header from a relative-path fetch — but anchors don't
    // forward custom headers, so we rely on /api/storage/files/{id}/download to
    // be reachable cross-origin via fetch + blob in downloadFile().
    downloadFile: async (id, fallbackName) => {
      const headers = {};
      const actor = window.getCurrentUserId();
      if (actor) headers["X-Demo-User-Id"] = actor;
      const res = await fetch(`${window.API_BASE}/api/storage/files/${id}/download`, { headers });
      if (!res.ok) {
        let message = `download ${id} → HTTP ${res.status}`;
        try { const data = await res.json(); if (data?.error) message = data.error; } catch { /* */ }
        const err = new Error(message); err.status = res.status; throw err;
      }
      const cd = res.headers.get("Content-Disposition") || "";
      const m = /filename\*?=(?:UTF-8'')?["']?([^"';]+)["']?/i.exec(cd);
      const name = decodeURIComponent(m?.[1] || fallbackName || "download");
      const blob = await res.blob();
      const url = URL.createObjectURL(blob);
      const a = document.createElement("a");
      a.href = url;
      a.download = name;
      document.body.appendChild(a);
      a.click();
      a.remove();
      setTimeout(() => URL.revokeObjectURL(url), 1000);
    },
  },
  // Boards / sprints / stories / work items (kanban)
  boards: {
    list:   ()      => request("GET",    "/api/boards"),
    get:    (id)    => request("GET",    `/api/boards/${id}`),
    create: (p)     => request("POST",   "/api/boards", p),
    update: (id, p) => request("PUT",    `/api/boards/${id}`, p),
    delete: (id)    => request("DELETE", `/api/boards/${id}`),
    view:   (id, sprintId) =>
      sprintId
        ? request("GET", `/api/boards/${id}/sprints/${sprintId}/view`)
        : request("GET", `/api/boards/${id}/backlog/view`),
  },
  boardStates: {
    list:    (boardId)             => request("GET",    `/api/boards/${boardId}/states`),
    add:     (boardId, p)          => request("POST",   `/api/boards/${boardId}/states`, p),
    update:  (boardId, stateId, p) => request("PUT",    `/api/boards/${boardId}/states/${stateId}`, p),
    delete:  (boardId, stateId, reassignTo) => request(
      "DELETE",
      `/api/boards/${boardId}/states/${stateId}` + (reassignTo ? `?reassignTo=${reassignTo}` : "")
    ),
    reorder: (boardId, stateIds)   => request("POST",   `/api/boards/${boardId}/states/reorder`, { stateIds }),
  },
  sprints: {
    listByBoard: (boardId)    => request("GET",    `/api/boards/${boardId}/sprints`),
    get:         (id)         => request("GET",    `/api/sprints/${id}`),
    create:      (boardId, p) => request("POST",   `/api/boards/${boardId}/sprints`, p),
    update:      (id, p)      => request("PUT",    `/api/sprints/${id}`, p),
    delete:      (id)         => request("DELETE", `/api/sprints/${id}`),
  },
  stories: {
    bySprint: (sprintId) => request("GET",    `/api/sprints/${sprintId}/stories`),
    backlog:  (boardId)  => request("GET",    `/api/boards/${boardId}/backlog`),
    get:      (id)       => request("GET",    `/api/stories/${id}`),
    create:   (p)        => request("POST",   "/api/stories", p),
    update:   (id, p)    => request("PUT",    `/api/stories/${id}`, p),
    delete:   (id)       => request("DELETE", `/api/stories/${id}`),
  },
  workItems: {
    byStory: (storyId)    => request("GET",    `/api/stories/${storyId}/items`),
    get:     (id)         => request("GET",    `/api/items/${id}`),
    create:  (storyId, p) => request("POST",   `/api/stories/${storyId}/items`, p),
    update:  (id, p)      => request("PUT",    `/api/items/${id}`, p),
    move:    (id, p)      => request("POST",   `/api/items/${id}/move`, p),
    delete:  (id)         => request("DELETE", `/api/items/${id}`),
  },
  // AI assistant — streams an SSE feed; consumer iterates with for-await-of.
  ai: {
    /**
     * Posts the current conversation to /api/ai/ask and returns an async
     * iterable of parsed SSE events: { type, ...payload }. The stream stays
     * open until the controller writes a `done` or `error` event.
     */
    ask: async function* (messages, lang) {
      const headers = { "Content-Type": "application/json", "Accept": "text/event-stream" };
      const actor = window.getCurrentUserId();
      if (actor) headers["X-Demo-User-Id"] = actor;
      const res = await fetch(`${window.API_BASE}/api/ai/ask`, {
        method: "POST",
        headers,
        body: JSON.stringify({ messages, lang }),
      });
      if (!res.ok || !res.body) {
        let message = `POST /api/ai/ask → HTTP ${res.status}`;
        try { const data = await res.json(); if (data?.error) message = data.error; } catch { /* not json */ }
        throw new Error(message);
      }
      const reader = res.body.getReader();
      const decoder = new TextDecoder("utf-8");
      let buf = "";
      while (true) {
        const { value, done } = await reader.read();
        if (done) return;
        buf += decoder.decode(value, { stream: true });
        // SSE frames are separated by blank lines. We accept both \n\n and \r\n\r\n.
        let sepIdx;
        while ((sepIdx = buf.search(/\r?\n\r?\n/)) !== -1) {
          const frame = buf.slice(0, sepIdx);
          buf = buf.slice(sepIdx).replace(/^\r?\n\r?\n/, "");
          let dataLine = "";
          for (const line of frame.split(/\r?\n/)) {
            if (line.startsWith("data:")) dataLine += line.slice(5).trimStart();
          }
          if (!dataLine) continue;
          try { yield JSON.parse(dataLine); } catch { /* malformed frame, skip */ }
        }
      }
    },
  },
  // Automations: stored AI prompts that fire on backend events (storage upload, etc.).
  automations: {
    list:      ()        => request("GET",    "/api/automations"),
    get:       (id)      => request("GET",    `/api/automations/${id}`),
    create:    (p)       => request("POST",   "/api/automations", p),
    update:    (id, p)   => request("PUT",    `/api/automations/${id}`, p),
    setActive: (id, isActive) => request("POST", `/api/automations/${id}/active`, { isActive }),
    delete:    (id)      => request("DELETE", `/api/automations/${id}`),
  },
  // Notion-like documentation: hierarchical pages with block-based content stored as JSON.
  pages: {
    tree:           ()        => request("GET",    "/api/pages"),
    trash:          ()        => request("GET",    "/api/pages/trash"),
    get:            (id)      => request("GET",    `/api/pages/${id}`),
    create:         (p)       => request("POST",   "/api/pages", p),
    rename:         (id, p)   => request("PUT",    `/api/pages/${id}/title`, p),
    setIcon:        (id, p)   => request("PUT",    `/api/pages/${id}/icon`, p),
    move:           (id, p)   => request("PUT",    `/api/pages/${id}/move`, p),
    updateContent:  (id, p)   => request("PUT",    `/api/pages/${id}/content`, p),
    sendToTrash:    (id)      => request("POST",   `/api/pages/${id}/trash`),
    restore:        (id)      => request("POST",   `/api/pages/${id}/restore`),
    deleteForever:  (id)      => request("DELETE", `/api/pages/${id}`),
  },
};

Object.assign(window, { api: window.api });
