/**
 * 议程项资料 + 会议级资料 + 下载 API service
 *
 * 端点编号：[114]–[121]（详见 docs/modules/meeting-attendance/07-api.md）
 *
 * 上传：multipart/form-data，字段 `file`（议程项级不带 taskId，service 内部 auto-flip）
 * 下载：流式响应，浏览器走 `window.open` / `<a download>` 触发即可
 */

import { api } from '@/lib/api';
import type {
  MeetingAgendaItemAttachment,
  MeetingAgendaItemUploadTask,
  UserBrief,
} from './agenda';

const BASE_URL = '/meeting-attendance';

const apiGet = <T = any>(url: string, config?: Record<string, any>) =>
  api.get(url, config) as Promise<T>;
const apiPost = <T = any>(url: string, payload?: any, config?: Record<string, any>) =>
  api.post(url, payload, config) as Promise<T>;
const apiDelete = <T = any>(url: string, config?: Record<string, any>) =>
  api.delete(url, config) as Promise<T>;

// ─── 类型 ───────────────────────────────────────────────────────────────

export type MeetingAttachmentCategory =
  | 'MINUTES'
  | 'MATERIAL'
  | 'PRESENTATION'
  | 'OTHER';

export interface MeetingAttachment {
  id: string;
  meetingId: string;
  uploadedById: string;
  uploadedBy: UserBrief;
  category?: MeetingAttachmentCategory | null;
  filename: string;
  mimeType: string;
  size: string;
  uploadedAt: string;
}

export interface UploadProgressEvent {
  loaded: number;
  total: number;
  /** 0-100 整数 */
  percent: number;
}

// ─── 议程项级资料（[114]–[116]）─────────────────────────────────────────

/**
 * [114] 上传议程项资料
 *
 * 不带 taskId —— 后端按 (item, currentUser, PENDING) 自动选最早一条任务 auto-flip。
 *
 * @param onProgress 上传进度回调（百分比）
 * @param signal AbortSignal 用于取消
 */
export function uploadAgendaItemAttachment(
  itemId: string,
  file: File,
  options?: {
    onProgress?: (e: UploadProgressEvent) => void;
    signal?: AbortSignal;
  },
) {
  const form = new FormData();
  form.append('file', file);
  return apiPost<{
    attachment: MeetingAgendaItemAttachment;
    taskUpdated?: MeetingAgendaItemUploadTask;
  }>(`${BASE_URL}/agenda-items/${itemId}/attachments`, form, {
    headers: { 'Content-Type': 'multipart/form-data' },
    onUploadProgress: (event: any) => {
      if (!options?.onProgress) return;
      const total = event.total ?? file.size;
      const loaded = event.loaded ?? 0;
      const percent = total > 0 ? Math.min(100, Math.round((loaded / total) * 100)) : 0;
      options.onProgress({ loaded, total, percent });
    },
    signal: options?.signal,
  });
}

/** [115] 列出议程项资料 */
export function listAgendaItemAttachments(itemId: string) {
  return apiGet<{ items: MeetingAgendaItemAttachment[] }>(
    `${BASE_URL}/agenda-items/${itemId}/attachments`,
  );
}

/** [116] 软删议程项资料 */
export function deleteAgendaItemAttachment(itemId: string, attachmentId: string) {
  return apiDelete<void>(
    `${BASE_URL}/agenda-items/${itemId}/attachments/${attachmentId}`,
  );
}

// ─── 会议级资料（[117]–[119]）───────────────────────────────────────────

export function uploadMeetingAttachment(
  meetingId: string,
  file: File,
  options?: {
    category?: MeetingAttachmentCategory;
    onProgress?: (e: UploadProgressEvent) => void;
    signal?: AbortSignal;
  },
) {
  const form = new FormData();
  form.append('file', file);
  if (options?.category) form.append('category', options.category);
  return apiPost<{ attachment: MeetingAttachment }>(
    `${BASE_URL}/meetings/${meetingId}/attachments`,
    form,
    {
      headers: { 'Content-Type': 'multipart/form-data' },
      onUploadProgress: (event: any) => {
        if (!options?.onProgress) return;
        const total = event.total ?? file.size;
        const loaded = event.loaded ?? 0;
        const percent = total > 0 ? Math.min(100, Math.round((loaded / total) * 100)) : 0;
        options.onProgress({ loaded, total, percent });
      },
      signal: options?.signal,
    },
  );
}

export function listMeetingAttachments(
  meetingId: string,
  params?: { category?: MeetingAttachmentCategory },
) {
  return apiGet<{ items: MeetingAttachment[] }>(
    `${BASE_URL}/meetings/${meetingId}/attachments`,
    { params },
  );
}

export function deleteMeetingAttachment(meetingId: string, attachmentId: string) {
  return apiDelete<void>(
    `${BASE_URL}/meetings/${meetingId}/attachments/${attachmentId}`,
  );
}

// ─── 下载（[120]–[121]）─────────────────────────────────────────────────

/**
 * 构造议程项资料下载 URL（用于 <a href> 或 window.open）
 *
 * 注意：流式响应 + RFC 5987 Content-Disposition；浏览器直接下载即可。
 * 后端走 cookie / Bearer 鉴权，前端 fetch 会带 token；但 <a> 直接跳走不会带 Authorization。
 * 因此采用 fetch + blob 下载的方式，确保走拦截器带 token。
 */
export async function downloadAgendaItemAttachment(
  attachmentId: string,
  filename: string,
) {
  const resp = (await apiGet<Blob>(
    `${BASE_URL}/attachments/agenda-item/${attachmentId}/download`,
    { responseType: 'blob' },
  )) as unknown as Blob;
  triggerBrowserDownload(resp, filename);
}

export async function downloadMeetingAttachment(attachmentId: string, filename: string) {
  const resp = (await apiGet<Blob>(
    `${BASE_URL}/attachments/meeting/${attachmentId}/download`,
    { responseType: 'blob' },
  )) as unknown as Blob;
  triggerBrowserDownload(resp, filename);
}

function triggerBrowserDownload(blob: Blob, filename: string) {
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = filename;
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  // 延迟 revoke 避免极少数浏览器在下载未触发完就被释放
  setTimeout(() => URL.revokeObjectURL(url), 1000);
}

export const attachmentApi = {
  uploadAgendaItemAttachment,
  listAgendaItemAttachments,
  deleteAgendaItemAttachment,
  uploadMeetingAttachment,
  listMeetingAttachments,
  deleteMeetingAttachment,
  downloadAgendaItemAttachment,
  downloadMeetingAttachment,
};

// ─── 共享：MIME 白名单 + 大小校验（前端先校验，后端 magic bytes 二次） ──────

/** 允许的 MIME 白名单（与后端契约对齐，8 项） */
export const ALLOWED_ATTACHMENT_MIME = new Set<string>([
  'application/pdf',
  'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
  'application/vnd.openxmlformats-officedocument.presentationml.presentation',
  'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  'image/jpeg',
  'image/png',
  'video/mp4',
  'video/quicktime',
]);

export const MAX_ATTACHMENT_SIZE = 200 * 1024 * 1024; // 200 MB

export type AttachmentValidateError =
  | 'ATTACHMENT_TOO_LARGE'
  | 'ATTACHMENT_MIME_NOT_ALLOWED';

export function validateAttachmentClient(file: File): AttachmentValidateError | null {
  if (file.size > MAX_ATTACHMENT_SIZE) return 'ATTACHMENT_TOO_LARGE';
  if (!ALLOWED_ATTACHMENT_MIME.has(file.type)) return 'ATTACHMENT_MIME_NOT_ALLOWED';
  return null;
}
