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

import { api } from "@/auth/http";

export interface ListConversationsParams {
  search?: string;
  unreadOnly?: boolean;
  page?: number;
  limit?: number;
}

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;
}

/**
 * Backoffice (agent) support-chat REST calls. The backoffice axios `api`
 * response interceptor already unwraps the `{ success, data }` envelope, so
 * `.data` here is the payload itself.
 */
export const supportApi = {
  listConversations: async (
    params: ListConversationsParams = {},
  ): Promise<SupportConversationListResult> => {
    const { data } = await api.get<SupportConversationListResult>(
      "/support/agent/conversations",
      { params },
    );
    return data;
  },
  listMessages: async (
    id: string,
    params: ListMessagesParams = {},
  ): Promise<SupportMessageListResult> => {
    const { data } = await api.get<SupportMessageListResult>(
      `/support/agent/conversations/${id}/messages`,
      { params },
    );
    return 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.
   * Same reason `filesService.download` exists for booking documents. The caller
   * wraps this blob in an object URL.
   */
  fetchAttachment: async (relativeUrl: string): Promise<Blob> => {
    // The DTO path is absolute from the API root (`/api/...`), but this client's
    // baseURL already ends in `/api` — drop the duplicate prefix.
    const path = relativeUrl.replace(/^\/api/, "");
    const { data } = await api.get(path, { responseType: "blob" });
    return data as unknown as Blob;
  },
  sendMessage: async (
    id: string,
    input: SendMessageInput,
  ): Promise<SupportMessageDto> => {
    const { data } = await api.post<SupportMessageDto>(
      `/support/agent/conversations/${id}/messages`,
      toRequestBody(input),
    );
    return data;
  },
  /** Open the thread with a company, or hand back the existing one. */
  startConversation: async (
    companyId: string,
  ): Promise<SupportConversationDto> => {
    const { data } = await api.post<SupportConversationDto>(
      "/support/agent/conversations",
      { companyId },
    );
    return data;
  },
  markRead: async (id: string): Promise<{ unreadCount: number }> => {
    const { data } = await api.post<{ unreadCount: number }>(
      `/support/agent/conversations/${id}/read`,
    );
    return data;
  },
  unreadCount: async (): Promise<number> => {
    const { data } = await api.get<{ unreadCount: number }>(
      "/support/agent/unread-count",
    );
    return data.unreadCount;
  },
};
