// ═════════════════════════════════════════════════════════════════════════════
//  lib/api/documents.api.ts
//  Mirrors my-audits.api.ts + audit-documents.api.ts patterns.
// ═════════════════════════════════════════════════════════════════════════════

import { fetchApi } from './http';
import type {
  DocumentRow,
  DocumentsListResponse,
  DocumentsAnalytics,
  AccessLogResponse,
  UploadDocumentPayload,
  UpdateDocumentPayload,
  UnlockResponse,
  DocumentStatus,
  RoleMember,
} from './types/documents.types';

const API_BASE_URL =
  process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3008/api';

// ─── Auth token — same lookup order the rest of the app uses ─────────────────
function getAuthToken(): string {
  if (typeof window === 'undefined') return '';
  return (
    localStorage.getItem('access_token') ||
    sessionStorage.getItem('access_token') ||
    localStorage.getItem('token') ||
    localStorage.getItem('authToken') ||
    ''
  );
}

function authHeaders(): HeadersInit {
  const token = getAuthToken();
  return token ? { Authorization: `Bearer ${token}` } : {};
}

// ═════════════════════════════════════════════════════════════════════════════
//   LIST / DETAIL / ANALYTICS
// ═════════════════════════════════════════════════════════════════════════════

export interface GetDocumentsParams {
  page?: number;
  limit?: number;
  search?: string;
  category?: string;
  status?: DocumentStatus | 'all';
  role_id?: number;
}

export async function getDocuments(
  params: GetDocumentsParams = {},
): Promise<DocumentsListResponse> {
  const qs = new URLSearchParams();
  if (params.page) qs.set('page', String(params.page));
  if (params.limit) qs.set('limit', String(params.limit));
  if (params.search) qs.set('search', params.search);
  if (params.category && params.category !== 'all')
    qs.set('category', params.category);
  if (params.status && params.status !== 'all')
    qs.set('status', params.status);
  if (params.role_id) qs.set('role_id', String(params.role_id));

  const qstr = qs.toString();
  const url = `${API_BASE_URL}/documents${qstr ? `?${qstr}` : ''}`;
  return fetchApi<DocumentsListResponse>(url);
}

export async function getDocumentById(id: number): Promise<DocumentRow> {
  return fetchApi<DocumentRow>(`${API_BASE_URL}/documents/${id}`);
}

export async function getDocumentsAnalytics(): Promise<DocumentsAnalytics> {
  return fetchApi<DocumentsAnalytics>(`${API_BASE_URL}/documents/analytics`);
}

/**
 * Every user belonging to any of the given roles — powers the
 * "who gets notified" checklist in the upload modal once role(s) are picked.
 * Returns [] for an empty role list (no network call).
 */
export async function getUsersForRoles(
  roleIds: number[],
): Promise<RoleMember[]> {
  if (roleIds.length === 0) return [];
  const qs = roleIds.join(',');
  return fetchApi<RoleMember[]>(
    `${API_BASE_URL}/documents/roles/users?role_ids=${encodeURIComponent(qs)}`,
  );
}

// ═════════════════════════════════════════════════════════════════════════════
//   ADMIN — UPLOAD (multipart with progress) / UPDATE / DELETE
// ═════════════════════════════════════════════════════════════════════════════

export interface UploadDocumentResponse extends DocumentRow { }

/**
 * XHR upload with progress — same shape as uploadAuditDocument /
 * uploadEntryEvidence. Uses FormData because the backend controller
 * uses FileInterceptor + multipart.
 */
export async function uploadDocument(
  payload: UploadDocumentPayload,
  onProgress?: (percent: number) => void,
): Promise<UploadDocumentResponse> {
  const url = `${API_BASE_URL}/documents`;

  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    const formData = new FormData();

    formData.append('file', payload.file);
    formData.append('title', payload.title);
    formData.append('category', payload.category);

    if (payload.description) {
      formData.append('description', payload.description);
    }

    // ─── Role assignment ────────────────────────────────────────────────
    // Send the array as JSON because multipart/form-data sends values
    // as strings.
    formData.append(
      'role_ids',
      JSON.stringify(payload.role_ids),
    );

    // ─── Notification recipients ───────────────────────────────────────
    if (payload.notify_user_ids !== undefined) {
      formData.append(
        'notify_user_ids',
        JSON.stringify(payload.notify_user_ids),
      );
    }

    // ─── Security ──────────────────────────────────────────────────────
    if (payload.require_otp !== undefined) {
      formData.append(
        'require_otp',
        payload.require_otp ? 'true' : 'false',
      );
    }

    if (payload.allow_download !== undefined) {
      formData.append(
        'allow_download',
        payload.allow_download ? 'true' : 'false',
      );
    }

    if (payload.password) {
      formData.append('password', payload.password);
    }

    if (payload.expiry_date) {
      formData.append('expiry_date', payload.expiry_date);
    }

    xhr.open('POST', url);

    const token = getAuthToken();

    if (token) {
      xhr.setRequestHeader(
        'Authorization',
        `Bearer ${token}`,
      );
    }

    xhr.timeout = 0; // no timeout — large uploads

    xhr.upload.onprogress = (e) => {
      if (e.lengthComputable && onProgress) {
        onProgress(
          Math.round((e.loaded / e.total) * 100),
        );
      }
    };

    xhr.onload = () => {
      if (xhr.status >= 200 && xhr.status < 300) {
        try {
          resolve(JSON.parse(xhr.responseText));
        } catch {
          reject(
            new Error('Invalid response from server'),
          );
        }
      } else {
        let msg = `Upload failed: ${xhr.status} ${xhr.statusText}`;

        try {
          const body = JSON.parse(xhr.responseText);

          if (body?.message) {
            msg = Array.isArray(body.message)
              ? body.message.join('; ')
              : body.message;
          }
        } catch {}

        reject(new Error(msg));
      }
    };

    xhr.onerror = () =>
      reject(
        new Error('Network error during upload'),
      );

    xhr.onabort = () =>
      reject(
        new Error('Upload aborted'),
      );

    xhr.send(formData);
  });
}
export async function updateDocument(
  id: number,
  payload: UpdateDocumentPayload,
): Promise<DocumentRow> {
  return fetchApi<DocumentRow>(`${API_BASE_URL}/documents/${id}`, {
    method: 'PATCH',
    body: JSON.stringify(payload),
  });
}

export async function deleteDocument(id: number): Promise<{ ok: true }> {
  return fetchApi<{ ok: true }>(`${API_BASE_URL}/documents/${id}`, {
    method: 'DELETE',
  });
}

// ═════════════════════════════════════════════════════════════════════════════
//   STAFF — OTP / UNLOCK / VIEW / DOWNLOAD
// ═════════════════════════════════════════════════════════════════════════════

export async function requestDocumentOtp(
  id: number,
): Promise<{ ok: true; expires_in_minutes: number }> {
  return fetchApi<{ ok: true; expires_in_minutes: number }>(
    `${API_BASE_URL}/documents/${id}/request-otp`,
    { method: 'POST' },
  );
}

export async function unlockDocument(
  id: number,
  body: { password?: string; otp?: string },
): Promise<UnlockResponse> {
  return fetchApi<UnlockResponse>(`${API_BASE_URL}/documents/${id}/unlock`, {
    method: 'POST',
    body: JSON.stringify(body),
  });
}

/**
 * Fetch a viewable blob URL for the document. The token comes from
 * unlockDocument(). Same pattern as fetchAuditDocumentBlob.
 */
export async function fetchDocumentBlob(
  id: number,
  token: string,
  mode: 'view' | 'download' = 'view',
): Promise<string> {
  const url = `${API_BASE_URL}/documents/${id}/${mode}?token=${encodeURIComponent(token)}`;
  const res = await fetch(url, { headers: authHeaders() });
  if (!res.ok) {
    let detail = '';
    try {
      detail = (await res.json())?.message || '';
    } catch { }
    throw new Error(
      `Failed to fetch document: ${res.status} ${res.statusText}${detail ? ` — ${detail}` : ''}`,
    );
  }
  const blob = await res.blob();
  return URL.createObjectURL(blob);
}

/**
 * Trigger a native "Save as" download for a document.
 * Uses the /download endpoint (401/403 if allow_download=0 or bad token).
 */
export async function downloadDocument(
  id: number,
  token: string,
  fallbackName = 'document',
): Promise<void> {
  const url = `${API_BASE_URL}/documents/${id}/download?token=${encodeURIComponent(token)}`;
  const res = await fetch(url, { headers: authHeaders() });
  if (!res.ok) throw new Error(`Download failed (${res.status})`);

  let filename = fallbackName;
  const cd = res.headers.get('Content-Disposition');
  const match = cd && /filename="?([^"]+)"?/.exec(cd);
  if (match?.[1]) filename = decodeURIComponent(match[1]);

  const blob = await res.blob();
  const objUrl = window.URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = objUrl;
  a.download = filename;
  document.body.appendChild(a);
  a.click();
  a.remove();
  window.URL.revokeObjectURL(objUrl);
}

// ═════════════════════════════════════════════════════════════════════════════
//   ADMIN — AUDIT LOG
// ═════════════════════════════════════════════════════════════════════════════

export interface GetAccessLogParams {
  page?: number;
  limit?: number;
  user_id?: number;
  action?: string;
  date_from?: string;
  date_to?: string;
}

export async function getDocumentAccessLog(
  id: number,
  params: GetAccessLogParams = {},
): Promise<AccessLogResponse> {
  const qs = new URLSearchParams();
  if (params.page) qs.set('page', String(params.page));
  if (params.limit) qs.set('limit', String(params.limit));
  if (params.user_id) qs.set('user_id', String(params.user_id));
  if (params.action) qs.set('action', params.action);
  if (params.date_from) qs.set('date_from', params.date_from);
  if (params.date_to) qs.set('date_to', params.date_to);

  const qstr = qs.toString();
  return fetchApi<AccessLogResponse>(
    `${API_BASE_URL}/documents/${id}/access-log${qstr ? `?${qstr}` : ''}`,
  );
}

export async function exportDocumentAccessLogCsv(
  id: number,
  fallbackName = 'access-log.csv',
): Promise<void> {
  const url = `${API_BASE_URL}/documents/${id}/access-log/export`;
  const res = await fetch(url, { headers: authHeaders() });
  if (!res.ok) throw new Error(`Export failed (${res.status})`);

  let filename = fallbackName;
  const cd = res.headers.get('Content-Disposition');
  const match = cd && /filename="?([^"]+)"?/.exec(cd);
  if (match?.[1]) filename = match[1];

  const blob = await res.blob();
  const objUrl = window.URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = objUrl;
  a.download = filename;
  document.body.appendChild(a);
  a.click();
  a.remove();
  window.URL.revokeObjectURL(objUrl);
}
