const fetch = require("node-fetch");

const GRAPH = "https://graph.microsoft.com/v1.0";

const MICROSOFT_SCOPES =
  "openid profile email User.Read offline_access Sites.ReadWrite.All";

const graph = async (accessToken, method, url, data = null) => {
  const m = String(method).toUpperCase();
  const opts = {
    method: m,
    headers: {
      Authorization: `Bearer ${accessToken}`,
      "Content-Type": "application/json",
    },
  };
  if (data != null && m !== "GET" && m !== "DELETE") {
    opts.body = JSON.stringify(data);
  }

  const res = await fetch(`${GRAPH}${url}`, opts);
  const text = await res.text();

  if (!res.ok) {
    const err = new Error(`Graph API ${res.status}`);
    try {
      err.response = { data: text ? JSON.parse(text) : text, status: res.status };
    } catch {
      err.response = { data: text, status: res.status };
    }
    throw err;
  }

  if (!text) return {};
  try {
    return JSON.parse(text);
  } catch {
    return text;
  }
};

/**
 * Refresh OAuth tokens (uses unified MICROSOFT_* env vars).
 */
const refreshToken = async (refreshTk) => {
  const clientId = process.env.MICROSOFT_CLIENT_ID;
  const clientSecret = process.env.MICROSOFT_CLIENT_SECRET;
  if (!clientId || !clientSecret) {
    throw new Error("MICROSOFT_CLIENT_ID / MICROSOFT_CLIENT_SECRET not configured");
  }

  const body = new URLSearchParams({
    client_id: clientId,
    client_secret: clientSecret,
    refresh_token: refreshTk,
    grant_type: "refresh_token",
    scope: MICROSOFT_SCOPES,
  }).toString();

  // Use tenant-specific endpoint for single-tenant apps
  const tenantId = process.env.MICROSOFT_TENANT_ID || "8fe3321f-727d-4c35-a7f7-452493485284";
  const res = await fetch(`https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`, {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body,
  });

  const json = await res.json().catch(() => ({}));
  if (!res.ok) {
    throw new Error(json.error_description || json.error || `Token refresh failed: ${res.status}`);
  }

  return {
    accessToken: json.access_token,
    refreshToken: json.refresh_token || refreshTk,
    expiresIn: json.expires_in || 3600,
  };
};

const getSiteId = async (accessToken) => {
  try {
    const res = await graph(accessToken, "get", "/sites/root");
    return res.id;
  } catch {
    return null;
  }
};

const createList = async (accessToken, siteId, listName, columns = []) => {
  const typeMap = {
    text: { text: {} },
    number: { number: {} },
    boolean: { boolean: {} },
    dateTime: { dateTime: { format: "dateTime" } },
    date: { dateTime: { format: "dateOnly" } },
    note: { text: { allowMultipleLines: true } },
  };

  const spColumns = columns.map((col) => ({
    name: col.name,
    ...(typeMap[col.type] || { text: {} }),
  }));

  return await graph(accessToken, "post", `/sites/${siteId}/lists`, {
    displayName: listName,
    columns: spColumns,
    list: { template: "genericList" },
  });
};

const createItem = async (accessToken, siteId, listId, fields) => {
  return await graph(accessToken, "post", `/sites/${siteId}/lists/${listId}/items`, {
    fields,
  });
};

const getItems = async (accessToken, siteId, listId, filter = null) => {
  let url = `/sites/${siteId}/lists/${listId}/items?expand=fields&$top=500`;
  if (filter) url += `&$filter=${filter}`;
  const res = await graph(accessToken, "get", url);
  return res.value.map((item) => ({
    spId: item.id,
    ...item.fields,
  }));
};

const updateItem = async (accessToken, siteId, listId, itemId, fields) => {
  return await graph(
    accessToken,
    "patch",
    `/sites/${siteId}/lists/${listId}/items/${itemId}/fields`,
    fields,
  );
};

const deleteItem = async (accessToken, siteId, listId, itemId) => {
  return await graph(
    accessToken,
    "delete",
    `/sites/${siteId}/lists/${listId}/items/${itemId}`,
  );
};

const getLists = async (accessToken, siteId) => {
  const res = await graph(accessToken, "get", `/sites/${siteId}/lists`);
  return res.value.map((l) => ({
    id: l.id,
    name: l.displayName,
    webUrl: l.webUrl || null,
  }));
};

module.exports = {
  graph,
  refreshToken,
  getSiteId,
  createList,
  createItem,
  getItems,
  updateItem,
  deleteItem,
  getLists,
  MICROSOFT_SCOPES,
};
