import type {
  SendSupportMessageResult,
  SupportConversationDto,
  SupportMessageListResult,
} from "@edr/types";

import { client } from "@/utils/api";

export interface ListMessagesParams {
  /** Opaque cursor from the previous page's `nextCursor`. */
  before?: string;
  limit?: number;
}

/** What the composer hands over: text, files, or both (never neither). */
export interface SendMessageInput {
  body?: string;
  attachments?: File[];
}

/**
 * A message with files goes as multipart so the server can persist them against
 * the message it creates in the same request; text-only stays JSON. Letting
 * axios set the multipart boundary itself is deliberate — setting
 * `Content-Type` by hand omits the boundary and the request fails to parse.
 */
function toRequestBody(input: SendMessageInput): FormData | { body?: string } {
  if (!input.attachments?.length) return { body: input.body };

  const form = new FormData();
  if (input.body) form.append("body", input.body);
  for (const file of input.attachments) form.append("attachments", file);
  return form;
}

/**
 * Portal support-chat REST calls. My company has exactly one thread, so nothing
 * here takes a conversation id — the server derives it from the caller.
 *
 * The portal axios `client` returns the raw response and the API wraps payloads
 * in a `{ success, data }` envelope, so we unwrap `.data.data` (same convention
 * as the other portal services).
 */
export const supportApi = {
  /** Null until someone has actually sent a message. */
  getConversation: async (): Promise<SupportConversationDto | null> => {
    const { data } = await client.get("/api/support/conversation");
    return data.data;
  },
  listMessages: async (
    params: ListMessagesParams = {},
  ): Promise<SupportMessageListResult> => {
    const { data } = await client.get("/api/support/conversation/messages", {
      params,
    });
    return data.data;
  },
  /**
   * Attachment bytes, fetched through the authenticated client.
   *
   * Deliberately not a direct `<img src={url}>`: the API guard reads the bearer
   * token from the Authorization header only — there is no cookie fallback — and
   * an `<img>` request cannot carry one, so a direct src is an unavoidable 401.
   * The same reason `filesService.download` exists for booking documents. The
   * caller wraps this blob in an object URL.
   *
   * `relativeUrl` is the DTO's `url` (`/api/support/attachments/:id`), so the
   * route stays owned by the server.
   */
  fetchAttachment: async (relativeUrl: string): Promise<Blob> => {
    const { data } = await client.get(relativeUrl, { responseType: "blob" });
    return data as Blob;
  },
  sendMessage: async (
    input: SendMessageInput,
  ): Promise<SendSupportMessageResult> => {
    const { data } = await client.post(
      "/api/support/conversation/messages",
      toRequestBody(input),
    );
    return data.data;
  },
  markRead: async (): Promise<{ unreadCount: number }> => {
    const { data } = await client.post("/api/support/conversation/read");
    return data.data;
  },
  unreadCount: async (): Promise<number> => {
    const { data } = await client.get("/api/support/unread-count");
    return data.data.unreadCount;
  },
};
