import { apiClient, API_BASE_URL } from "./client";

async function fetchApi<T>(
  endpoint: string,
  options: { method?: string; data?: unknown } = {}
): Promise<T> {
  const { method = "GET", data } = options;

  try {
    const response = await apiClient({
      url: endpoint,
      method,
      data,
    });

    if (response.data.success && response.data.data !== undefined) {
      return response.data.data as T;
    }

    return response.data as T;
  } catch (error: unknown) {
    if (error && typeof error === "object" && "response" in error) {
      const axiosError = error as {
        response?: { data?: { message?: string }; status?: number };
      };
      throw new Error(
        axiosError.response?.data?.message || `HTTP ${axiosError.response?.status}`
      );
    }
    throw new Error("Unknown error");
  }
}

export interface UserMailAccount {
  id: number;
  displayName: string;
  email: string;
  protocol: "imap" | "pop3";
  host: string;
  port: number;
  useTls: boolean;
  username: string;
  status: string;
  createdAt: string;
  updatedAt: string;
}

export interface CreateUserMailAccountDto {
  displayName: string;
  email: string;
  protocol: "imap" | "pop3";
  host: string;
  port: number;
  useTls: boolean;
  username: string;
  password: string;
}

export interface ReceivedMail {
  id: string;
  messageId: string;
  from: string;
  to: string;
  subject: string;
  date: string;
  preview: string;
  isRead: boolean;
  hasAttachments: boolean;
}

export interface MailDetail extends ReceivedMail {
  htmlBody: string;
  textBody: string;
  cc?: string;
  bcc?: string;
  attachments: Array<{ filename: string; contentType: string; size: number }>;
}

export async function getUserMailAccounts(): Promise<UserMailAccount[]> {
  return fetchApi<UserMailAccount[]>("/user-mail/accounts");
}

export async function createUserMailAccount(
  dto: CreateUserMailAccountDto
): Promise<UserMailAccount> {
  return fetchApi<UserMailAccount>("/user-mail/accounts", {
    method: "POST",
    data: dto,
  });
}

export async function updateUserMailAccount(
  id: number,
  dto: Partial<CreateUserMailAccountDto>
): Promise<UserMailAccount> {
  return fetchApi<UserMailAccount>(`/user-mail/accounts/${id}`, {
    method: "PUT",
    data: dto,
  });
}

export async function deleteUserMailAccount(id: number): Promise<void> {
  return fetchApi<void>(`/user-mail/accounts/${id}`, { method: "DELETE" });
}

export async function testUserMailAccount(
  id: number
): Promise<{ success: boolean; message: string }> {
  return fetchApi<{ success: boolean; message: string }>(
    `/user-mail/accounts/${id}/test`,
    { method: "POST" }
  );
}

export async function testUserMailConnectionDirect(dto: {
  protocol: string;
  host: string;
  port: number;
  useTls: boolean;
  username: string;
  password: string;
}): Promise<{ success: boolean; message: string }> {
  return fetchApi<{ success: boolean; message: string }>(
    `/user-mail/test-connection`,
    { method: "POST", data: dto }
  );
}

export async function getUserMails(
  accountId: number,
  limit: number = 50
): Promise<ReceivedMail[]> {
  return fetchApi<ReceivedMail[]>(
    `/user-mail/accounts/${accountId}/mails?limit=${limit}`
  );
}

export async function getUserMailDetail(
  accountId: number,
  seqno: number
): Promise<MailDetail> {
  return fetchApi<MailDetail>(
    `/user-mail/accounts/${accountId}/mails/${seqno}`
  );
}

export async function markUserMailAsRead(
  accountId: number,
  seqno: number
): Promise<void> {
  return fetchApi<void>(
    `/user-mail/accounts/${accountId}/mails/${seqno}/mark-read`,
    { method: "POST" }
  );
}

export async function deleteUserMail(
  accountId: number,
  seqno: number
): Promise<void> {
  return fetchApi<void>(`/user-mail/accounts/${accountId}/mails/${seqno}`, {
    method: "DELETE",
  });
}

export function streamUserMails(
  accountId: number,
  limit: number = 20,
  before: number | null = null,
  onMail: (mail: ReceivedMail) => void,
  onDone: () => void,
  onError: (err: string) => void
): () => void {
  const token = typeof window !== "undefined" ? localStorage.getItem("authToken") : null;
  const url = before
    ? `${API_BASE_URL}/user-mail/accounts/${accountId}/mails/stream?limit=${limit}&before=${before}`
    : `${API_BASE_URL}/user-mail/accounts/${accountId}/mails/stream?limit=${limit}`;

  const controller = new AbortController();

  (async () => {
    try {
      const res = await fetch(url, {
        headers: { Authorization: `Bearer ${token}` },
        signal: controller.signal,
      });

      if (!res.ok || !res.body) {
        onError("스트리밍 연결 실패");
        return;
      }

      const reader = res.body.getReader();
      const decoder = new TextDecoder();
      let buffer = "";

      while (true) {
        const { done, value } = await reader.read();
        if (done) break;

        buffer += decoder.decode(value, { stream: true });
        const lines = buffer.split("\n");
        buffer = lines.pop() || "";

        let currentEvent = "message";
        for (const line of lines) {
          if (line.startsWith("event: ")) {
            currentEvent = line.slice(7).trim();
            if (currentEvent === "done") onDone();
          } else if (line.startsWith("data: ")) {
            if (currentEvent === "message") {
              try {
                const mail = JSON.parse(line.slice(6));
                if (mail?.id) onMail(mail);
              } catch {}
            } else if (currentEvent === "error") {
              try {
                const { message } = JSON.parse(line.slice(6));
                onError(message || "스트리밍 오류");
              } catch {}
            }
            currentEvent = "message"; // 리셋
          }
        }
      }
    } catch (e: any) {
      if (e.name !== "AbortError") onError(e.message || "연결 오류");
    }
  })();

  return () => controller.abort();
}

export interface MailFolder {
  path: string;
  name: string;
  unseen: number;
}

export interface SendMailDto {
  to: string;
  cc?: string;
  subject: string;
  html: string;
  text?: string;
  inReplyTo?: string;
  references?: string;
}

export interface MailAttachment {
  partId: string;
  filename: string;
  contentType: string;
  size: number;
}

export async function getUserMailFolders(accountId: number): Promise<MailFolder[]> {
  return fetchApi<MailFolder[]>(`/user-mail/accounts/${accountId}/folders`);
}

export async function moveUserMail(
  accountId: number,
  seqno: number,
  targetFolder: string
): Promise<{ success: boolean; message: string }> {
  return fetchApi<{ success: boolean; message: string }>(
    `/user-mail/accounts/${accountId}/mails/${seqno}/move`,
    { method: 'POST', data: { targetFolder } }
  );
}

export async function sendUserMail(
  accountId: number,
  dto: SendMailDto
): Promise<{ success: boolean; message: string }> {
  return fetchApi<{ success: boolean; message: string }>(
    `/user-mail/accounts/${accountId}/send`,
    { method: 'POST', data: dto }
  );
}

export async function getUserMailAttachments(
  accountId: number,
  seqno: number,
  folder: string = 'INBOX'
): Promise<MailAttachment[]> {
  return fetchApi<MailAttachment[]>(
    `/user-mail/accounts/${accountId}/mails/${seqno}/attachments?folder=${encodeURIComponent(folder)}`
  );
}

export async function downloadAttachment(
  accountId: number,
  seqno: number,
  partId: string,
  filename: string,
  folder: string = 'INBOX',
  onProgress?: (percent: number) => void,
  totalSize?: number
): Promise<void> {
  const token = typeof window !== 'undefined' ? localStorage.getItem('authToken') : null;
  const url = `${API_BASE_URL}/user-mail/accounts/${accountId}/mails/${seqno}/attachment/${encodeURIComponent(partId)}?folder=${encodeURIComponent(folder)}`;
  const res = await fetch(url, { headers: { Authorization: `Bearer ${token}` } });
  if (!res.ok) throw new Error(`다운로드 실패: ${res.status}`);

  const contentLength = totalSize || parseInt(res.headers.get('content-length') || '0');

  if (contentLength && onProgress && res.body) {
    const reader = res.body.getReader();
    const chunks: Uint8Array[] = [];
    let received = 0;
    while (true) {
      const { done, value } = await reader.read();
      if (done) break;
      chunks.push(value);
      received += value.length;
      onProgress(Math.min(99, Math.round((received / contentLength) * 100)));
    }
    onProgress(100);
    const blob = new Blob(chunks);
    const a = document.createElement('a');
    a.href = URL.createObjectURL(blob);
    a.download = filename;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    setTimeout(() => URL.revokeObjectURL(a.href), 1000);
  } else {
    const blob = await res.blob();
    const a = document.createElement('a');
    a.href = URL.createObjectURL(blob);
    a.download = filename;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    setTimeout(() => URL.revokeObjectURL(a.href), 1000);
  }
}

export function streamFolderMails(
  accountId: number,
  folder: string,
  limit: number = 20,
  before: number | null = null,
  onMail: (mail: ReceivedMail) => void,
  onDone: () => void,
  onError: (err: string) => void
): () => void {
  const token = typeof window !== 'undefined' ? localStorage.getItem('authToken') : null;
  const encodedFolder = encodeURIComponent(folder);
  const url = before
    ? `${API_BASE_URL}/user-mail/accounts/${accountId}/folders/${encodedFolder}/mails/stream?limit=${limit}&before=${before}`
    : `${API_BASE_URL}/user-mail/accounts/${accountId}/folders/${encodedFolder}/mails/stream?limit=${limit}`;

  const controller = new AbortController();

  (async () => {
    try {
      const res = await fetch(url, {
        headers: { Authorization: `Bearer ${token}` },
        signal: controller.signal,
      });

      if (!res.ok || !res.body) { onError('스트리밍 연결 실패'); return; }

      const reader = res.body.getReader();
      const decoder = new TextDecoder();
      let buffer = '';

      while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        buffer += decoder.decode(value, { stream: true });
        const lines = buffer.split('\n');
        buffer = lines.pop() || '';

        let currentEvent = 'message';
        for (const line of lines) {
          if (line.startsWith('event: ')) {
            currentEvent = line.slice(7).trim();
            if (currentEvent === 'done') onDone();
          } else if (line.startsWith('data: ')) {
            if (currentEvent === 'message') {
              try { const mail = JSON.parse(line.slice(6)); if (mail?.id) onMail(mail); } catch {}
            } else if (currentEvent === 'error') {
              try { const { message } = JSON.parse(line.slice(6)); onError(message || '오류'); } catch {}
            }
            currentEvent = 'message';
          }
        }
      }
    } catch (e: any) {
      if (e.name !== 'AbortError') onError(e.message || '연결 오류');
    }
  })();

  return () => controller.abort();
}
