import { getAccessToken } from "./store.js";

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

type GraphResponse<T> = { value?: T[]; "@odata.nextLink"?: string };

export type MailMessage = {
  id: string;
  subject: string;
  from?: { emailAddress?: { name?: string; address?: string } };
  toRecipients?: Array<{ emailAddress?: { name?: string; address?: string } }>;
  receivedDateTime?: string;
  bodyPreview?: string;
  body?: { contentType?: string; content?: string };
  isRead?: boolean;
  hasAttachments?: boolean;
  importance?: string;
};

export type MailFolder = {
  id: string;
  displayName: string;
  totalItemCount?: number;
  unreadItemCount?: number;
};

async function graphFetch<T>(path: string, init?: RequestInit): Promise<T> {
  const token = await getAccessToken();
  const response = await fetch(`${GRAPH_ROOT}${path}`, {
    ...init,
    headers: {
      Authorization: `Bearer ${token}`,
      "Content-Type": "application/json",
      ...init?.headers,
    },
  });
  if (!response.ok) {
    const text = await response.text().catch(() => "");
    throw new Error(`Graph API ${path} failed (${response.status}): ${text || "unknown error"}`);
  }
  // Microsoft Graph returns 202 Accepted (empty body) for action endpoints
  // like /me/sendMail and /me/messages/{id}/reply, and 204 No Content for
  // others. Read the body as text first so any no-body success status
  // resolves to {} instead of throwing "Unexpected end of JSON input".
  const text = await response.text();
  if (!text) {
    return {} as T;
  }
  return JSON.parse(text) as T;
}

export async function listMessages(params?: {
  folder?: string;
  top?: number;
  skip?: number;
  filter?: string;
  search?: string;
}): Promise<MailMessage[]> {
  const folder = params?.folder ?? "inbox";
  const top = params?.top ?? 10;
  const select =
    "id,subject,from,toRecipients,receivedDateTime,bodyPreview,isRead,hasAttachments,importance";

  const queryParts = [`$top=${top}`, `$select=${select}`, "$orderby=receivedDateTime desc"];

  if (params?.skip) {
    queryParts.push(`$skip=${params.skip}`);
  }
  if (params?.filter) {
    queryParts.push(`$filter=${encodeURIComponent(params.filter)}`);
  }
  if (params?.search) {
    queryParts.push(`$search="${encodeURIComponent(params.search)}"`);
  }

  const query = queryParts.join("&");
  const result = await graphFetch<GraphResponse<MailMessage>>(
    `/me/mailFolders/${encodeURIComponent(folder)}/messages?${query}`,
  );
  return result.value ?? [];
}

export async function readMessage(messageId: string): Promise<MailMessage> {
  return await graphFetch<MailMessage>(
    `/me/messages/${encodeURIComponent(messageId)}?$select=id,subject,from,toRecipients,receivedDateTime,body,isRead,hasAttachments,importance`,
  );
}

export async function sendMessage(params: {
  to: string[];
  cc?: string[];
  subject: string;
  body: string;
  contentType?: "Text" | "HTML";
}): Promise<void> {
  const message = {
    subject: params.subject,
    body: {
      contentType: params.contentType ?? "Text",
      content: params.body,
    },
    toRecipients: params.to.map((addr) => ({
      emailAddress: { address: addr },
    })),
    ...(params.cc?.length
      ? {
          ccRecipients: params.cc.map((addr) => ({
            emailAddress: { address: addr },
          })),
        }
      : {}),
  };

  await graphFetch<void>("/me/sendMail", {
    method: "POST",
    body: JSON.stringify({ message, saveToSentItems: true }),
  });
}

export async function replyToMessage(params: {
  messageId: string;
  body: string;
  replyAll?: boolean;
}): Promise<void> {
  const action = params.replyAll ? "replyAll" : "reply";
  await graphFetch<void>(`/me/messages/${encodeURIComponent(params.messageId)}/${action}`, {
    method: "POST",
    body: JSON.stringify({ comment: params.body }),
  });
}

export async function searchMessages(query: string, top?: number): Promise<MailMessage[]> {
  return listMessages({ search: query, top: top ?? 10 });
}

export async function listFolders(): Promise<MailFolder[]> {
  const result = await graphFetch<GraphResponse<MailFolder>>(
    "/me/mailFolders?$select=id,displayName,totalItemCount,unreadItemCount&$top=50",
  );
  return result.value ?? [];
}

export async function markAsRead(messageId: string): Promise<void> {
  await graphFetch<void>(`/me/messages/${encodeURIComponent(messageId)}`, {
    method: "PATCH",
    body: JSON.stringify({ isRead: true }),
  });
}

export async function moveMessage(messageId: string, destinationFolder: string): Promise<void> {
  await graphFetch<void>(`/me/messages/${encodeURIComponent(messageId)}/move`, {
    method: "POST",
    body: JSON.stringify({ destinationId: destinationFolder }),
  });
}
