/**
 * API client using native Fetch API.
 * All requests go to /api (proxied to backend by Vite in dev, or nginx in prod).
 *
 * Phase 2 additions (D-11):
 *   - Injects Authorization: Bearer header from useAuthStore().accessToken
 *   - On 401: calls authStore.refresh() and retries once (_retry guard)
 *   - On refresh failure: clears accessToken, throws 'Session expired'
 */

async function request(path, options = {}) {
  // Lazy import to avoid circular dependency (stores/auth.js → api/client.js → stores/auth.js)
  const { useAuthStore } = await import('../stores/auth.js')
  const authStore = useAuthStore()

  const headers = { ...(options.headers || {}) }
  if (authStore.accessToken) {
    headers['Authorization'] = `Bearer ${authStore.accessToken}`
  }

  const res = await fetch(path, { ...options, headers, credentials: 'include' })

  // 401 → attempt refresh → retry once
  // Skip refresh for auth endpoints: login/register return 401 for bad credentials (not expired tokens),
  // and refresh itself must not retry to avoid an infinite loop.
  const noRefreshPaths = ['/api/auth/login', '/api/auth/register', '/api/auth/refresh']
  if (res.status === 401 && !options._retry && !noRefreshPaths.includes(path)) {
    try {
      await authStore.refresh()
      return request(path, { ...options, _retry: true })
    } catch {
      authStore.accessToken = null
      authStore.user = null
      throw new Error('Session expired')
    }
  }

  if (!res.ok) {
    let msg = `HTTP ${res.status}`
    let payload = null
    try {
      const body = await res.json()
      if (typeof body.detail === 'object' && body.detail !== null) {
        payload = body.detail
        msg = body.detail.message || `HTTP ${res.status}`
      } else {
        msg = body.detail || msg
      }
    } catch {}
    const err = new Error(msg)
    err.status = res.status
    if (payload) err.payload = payload
    throw err
  }
  if (res.status === 204 || res.headers.get('content-length') === '0') return null
  return res.json()
}

// ── Documents ────────────────────────────────────────────────────────────────

export function listDocuments({ topic, page = 1, perPage = 20, folderId = null, q = null, sort = null, order = null } = {}) {
  const params = new URLSearchParams({ page, per_page: perPage })
  if (topic) params.set('topic', topic)
  if (folderId != null) params.set('folder_id', folderId)
  if (q) params.set('q', q)
  if (sort) params.set('sort', sort)
  if (order) params.set('order', order)
  return request(`/api/documents?${params}`)
}

export function getDocument(id) {
  return request(`/api/documents/${id}`)
}

export function deleteDocument(id, removeOnly = false) {
  const url = removeOnly ? `/api/documents/${id}?remove_only=true` : `/api/documents/${id}`
  return request(url, { method: 'DELETE' })
}

export function deleteDocumentRemoveOnly(id) {
  return deleteDocument(id, true)
}

export function classifyDocument(id, topics = null) {
  return request(`/api/documents/${id}/classify`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(topics ? { topics } : {}),
  })
}

export function getUploadUrl(filename, contentType) {
  return request('/api/documents/upload-url', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ filename, content_type: contentType }),
  })
}

export function confirmUpload(documentId) {
  return request(`/api/documents/${documentId}/confirm`, { method: 'POST' })
}

export function uploadToCloud(file, provider, folderPath) {
  const form = new FormData()
  form.append('file', file)
  form.append('target_backend', provider)
  if (folderPath) form.append('cloud_folder_path', folderPath)
  return request('/api/documents/upload', { method: 'POST', body: form })
}

// ── Topics ───────────────────────────────────────────────────────────────────

export function listTopics() {
  return request('/api/topics')
}

export function createTopic({ name, description = '', color = '#6366f1' }) {
  return request('/api/topics', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ name, description, color }),
  })
}

export function updateTopic(id, patch) {
  return request(`/api/topics/${id}`, {
    method: 'PATCH',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(patch),
  })
}

export function deleteTopic(id) {
  return request(`/api/topics/${id}`, { method: 'DELETE' })
}

export function suggestTopics(documentId) {
  return request('/api/topics/suggest', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ document_id: documentId }),
  })
}

// ── Quota ────────────────────────────────────────────────────────────────────

export function getMyQuota() {
  return request('/api/auth/me/quota')
}

// ── Auth ─────────────────────────────────────────────────────────────────────

export function login(body) {
  return request('/api/auth/login', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(body),
  })
}

export function register(body) {
  return request('/api/auth/register', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(body),
  })
}

export function refreshToken() {
  // No body — httpOnly cookie sent automatically via credentials: 'include'
  return request('/api/auth/refresh', { method: 'POST' })
}

export function logout() {
  return request('/api/auth/logout', { method: 'POST' })
}

export function logoutAll() {
  return request('/api/auth/logout-all', { method: 'POST' })
}

export function getMe() {
  return request('/api/auth/me')
}

export function changePassword(body) {
  return request('/api/auth/change-password', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(body),
  })
}

// ── TOTP ──────────────────────────────────────────────────────────────────────

export function totpSetup() {
  return request('/api/auth/totp/setup')
}

export function totpEnable(code) {
  return request('/api/auth/totp/enable', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ code }),
  })
}

export function totpDisable() {
  return request('/api/auth/totp', { method: 'DELETE' })
}

// ── Password reset ────────────────────────────────────────────────────────────

export function passwordResetRequest(email) {
  return request('/api/auth/password-reset', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ email }),
  })
}

export function passwordResetConfirm(token, newPassword) {
  return request('/api/auth/password-reset/confirm', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ token, new_password: newPassword }),
  })
}

// ── Admin ─────────────────────────────────────────────────────────────────────

export function adminListUsers() {
  return request('/api/admin/users')
}

export function adminCreateUser(body) {
  return request('/api/admin/users', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(body),
  })
}

export function adminDeactivateUser(id) {
  return request(`/api/admin/users/${id}/status`, {
    method: 'PATCH',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ is_active: false }),
  })
}

export function adminReactivateUser(id) {
  return request(`/api/admin/users/${id}/status`, {
    method: 'PATCH',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ is_active: true }),
  })
}

export function adminResetUserPassword(id) {
  return request(`/api/admin/users/${id}/password-reset`, { method: 'POST' })
}

export function adminGetUserQuota(id) {
  return request(`/api/admin/users/${id}/quota`)
}

export function adminUpdateQuota(id, limitBytes) {
  return request(`/api/admin/users/${id}/quota`, {
    method: 'PATCH',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ limit_bytes: limitBytes }),
  })
}

export function adminUpdateAiConfig(id, provider, model) {
  return request(`/api/admin/users/${id}/ai-config`, {
    method: 'PATCH',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ ai_provider: provider, ai_model: model }),
  })
}

export function adminDeleteUser(id, adminPassword) {
  return request(`/api/admin/users/${id}`, {
    method: 'DELETE',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ admin_password: adminPassword }),
  })
}

// ── Folders ───────────────────────────────────────────────────────────────────

export function listFolders(parentId = null) {
  const params = new URLSearchParams()
  if (parentId != null) params.set('parent_id', parentId)
  const qs = params.toString()
  return request(`/api/folders${qs ? `?${qs}` : ''}`)
}

export function createFolder(name, parentId = null) {
  return request('/api/folders', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ name, parent_id: parentId || null }),
  })
}

export function getFolder(folderId) {
  return request(`/api/folders/${folderId}`)
}

export function renameFolder(folderId, name) {
  return request(`/api/folders/${folderId}`, {
    method: 'PATCH',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ name }),
  })
}

export function deleteFolder(folderId) {
  return request(`/api/folders/${folderId}`, { method: 'DELETE' })
}

export function moveDocument(docId, folderId) {
  return request(`/api/documents/${docId}/folder`, {
    method: 'PATCH',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ folder_id: folderId || null }),
  })
}

// ── Shares ────────────────────────────────────────────────────────────────────

export function createShare(docId, recipientHandle, permission = 'view') {
  return request('/api/shares', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ document_id: docId, recipient_handle: recipientHandle, permission }),
  })
}

export function updateSharePermission(shareId, permission) {
  return request(`/api/shares/${shareId}`, {
    method: 'PATCH',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ permission }),
  })
}

export function listShares(docId) {
  const params = new URLSearchParams({ document_id: docId })
  return request(`/api/shares?${params}`)
}

export function deleteShare(shareId) {
  return request(`/api/shares/${shareId}`, { method: 'DELETE' })
}

export function getSharedWithMe() {
  return request('/api/shares/received')
}

// ── Preferences ───────────────────────────────────────────────────────────────

export function getMyPreferences() {
  return request('/api/auth/me/preferences')
}

export function updateMyPreferences(payload) {
  return request('/api/auth/me/preferences', {
    method: 'PATCH',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(payload),
  })
}

// ── Audit Log ─────────────────────────────────────────────────────────────────

export function adminListAuditLog({ start, end, user_handle, event_type, page = 1, per_page = 50 } = {}) {
  const params = new URLSearchParams()
  if (start) params.set('start', start)
  if (end) params.set('end', end)
  if (user_handle) params.set('user_handle', user_handle)
  if (event_type) params.set('event_type', event_type)
  params.set('page', page)
  params.set('per_page', per_page)
  return request(`/api/admin/audit-log?${params}`)
}

/**
 * Export the audit log as a CSV file using fetch + Blob URL.
 *
 * Unlike window.location.href, this sends the Authorization Bearer header so
 * the endpoint can authenticate the request (D-13, T-06.2-04-03).
 * Must NOT call res.json() — CSV is text/csv (Pitfall 5).
 */
export async function adminExportAuditLogCsv(params = {}, _retry = false) {
  const { useAuthStore } = await import('../stores/auth.js')
  const authStore = useAuthStore()

  const searchParams = new URLSearchParams({ format: 'csv' })
  if (params.start) searchParams.set('start', params.start)
  if (params.end) searchParams.set('end', params.end)
  if (params.user_handle) searchParams.set('user_handle', params.user_handle)
  if (params.event_type) searchParams.set('event_type', params.event_type)

  const headers = {}
  if (authStore.accessToken) {
    headers['Authorization'] = `Bearer ${authStore.accessToken}`
  }

  const res = await fetch(`/api/admin/audit-log/export?${searchParams}`, {
    headers,
    credentials: 'include',
  })

  if (res.status === 401 && !_retry) {
    try {
      await authStore.refresh()
      return adminExportAuditLogCsv(params, true)
    } catch {
      authStore.accessToken = null
      authStore.user = null
      throw new Error('Session expired')
    }
  }

  if (!res.ok) throw new Error(`Export failed: ${res.status}`)

  const text = await res.text()
  const blob = new Blob([text], { type: 'text/csv' })
  const url = URL.createObjectURL(blob)
  const a = document.createElement('a')
  a.href = url
  a.download = 'audit-export.csv'
  document.body.appendChild(a)
  a.click()
  document.body.removeChild(a)
  setTimeout(() => URL.revokeObjectURL(url), 1000)
}

/**
 * List available Celery daily audit export files from the MinIO audit-logs bucket.
 *
 * Returns: { items: [{ date: "YYYY-MM-DD", key: "audit-logs/YYYY-MM-DD.csv" }] }
 * Items are sorted descending by date.
 * Routes through request() which has built-in 401-refresh-retry logic.
 */
export function adminListDailyExports() {
  return request('/api/admin/audit-log/daily-exports')
}

/**
 * Download a specific Celery daily audit export file from MinIO using fetch + Blob URL.
 *
 * Uses the same fetch+Blob pattern as adminExportAuditLogCsv to send the
 * Authorization Bearer header (D-17, T-06.2-04-03).
 *
 * @param {string} date — YYYY-MM-DD format date string
 */
export async function adminDownloadDailyExport(date, _retry = false) {
  const { useAuthStore } = await import('../stores/auth.js')
  const authStore = useAuthStore()

  const headers = {}
  if (authStore.accessToken) {
    headers['Authorization'] = `Bearer ${authStore.accessToken}`
  }

  const res = await fetch(`/api/admin/audit-log/daily-exports/${date}`, {
    headers,
    credentials: 'include',
  })

  if (res.status === 401 && !_retry) {
    try {
      await authStore.refresh()
      return adminDownloadDailyExport(date, true)
    } catch {
      authStore.accessToken = null
      authStore.user = null
      throw new Error('Session expired')
    }
  }

  if (!res.ok) throw new Error(`Download failed: ${res.status}`)

  const text = await res.text()
  const blob = new Blob([text], { type: 'text/csv' })
  const url = URL.createObjectURL(blob)
  const a = document.createElement('a')
  a.href = url
  a.download = `audit-${date}.csv`
  document.body.appendChild(a)
  a.click()
  document.body.removeChild(a)
  setTimeout(() => URL.revokeObjectURL(url), 1000)
}

// ── Document content proxy URL ────────────────────────────────────────────────

export function getDocumentContentUrl(docId) {
  return `/api/documents/${docId}/content`
}

/**
 * Fetch document content bytes with authentication, returning the raw Response.
 *
 * Unlike request(), this function does NOT call res.json() — it returns the raw
 * Response so callers can call .blob() to build an object URL for iframe preview
 * or window.open() without an unauthenticated src= attribute.
 *
 * On 401: attempts one token refresh via authStore.refresh() then retries.
 * On refresh failure: clears auth state and throws 'Session expired'.
 *
 * Security: closes the unauthenticated content-access gap where an iframe src=
 * or window.open() with a raw /content URL would bypass the Bearer auth check
 * in cases where the browser does not send the cookie (cross-origin, incognito).
 * See plan 05-09 trust boundary: frontend→/api/documents/{id}/content.
 */
export async function fetchDocumentContent(docId, options = {}) {
  const { useAuthStore } = await import('../stores/auth.js')
  const authStore = useAuthStore()

  const headers = {}
  if (authStore.accessToken) {
    headers['Authorization'] = `Bearer ${authStore.accessToken}`
  }

  const res = await fetch(`/api/documents/${docId}/content`, {
    headers,
    credentials: 'include',
  })

  if (res.status === 401 && !options._retry) {
    try {
      await authStore.refresh()
      return fetchDocumentContent(docId, { _retry: true })
    } catch {
      authStore.accessToken = null
      authStore.user = null
      throw new Error('Session expired')
    }
  }

  if (!res.ok) {
    throw new Error(`Failed to fetch document content: ${res.status}`)
  }
  return res
}

// ── Cloud Storage ─────────────────────────────────────────────────────────────

export function listCloudConnections() {
  return request('/api/cloud/connections')
}

export function disconnectCloud(id) {
  return request(`/api/cloud/connections/${id}`, { method: 'DELETE' })
}

export function connectWebDav(provider, serverUrl, username, password) {
  return request('/api/cloud/connections/webdav', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ provider, server_url: serverUrl, username, password }),
  })
}

export function updateDefaultStorage(backend) {
  return request('/api/users/me/default-storage', {
    method: 'PATCH',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ backend }),
  })
}

export function getCloudFolders(provider, folderId) {
  return request(`/api/cloud/folders/${provider}/${folderId}`)
}

/**
 * Initiate OAuth flow for Google Drive or OneDrive.
 *
 * Returns a JSON object {url: "<authorization_url>"} from the backend.
 * The caller is responsible for navigating: window.location.href = data.url
 *
 * Using request() (not bare window.location.href) ensures the Bearer header
 * is injected and the 401→refresh retry path fires if the token has expired.
 * See plan 05-10 trust boundary: frontend→/api/cloud/oauth/initiate/{provider}.
 */
export function initiateOAuth(provider) {
  return request(`/api/cloud/oauth/initiate/${provider}`)
}

/**
 * Fetch non-secret configuration for a WebDAV/Nextcloud connection (edit flow).
 *
 * Returns {id, provider, server_url, connection_username} — never the password.
 * Used to pre-populate the Edit modal when re-editing an existing connection.
 */
export function getConnectionConfig(connectionId) {
  return request(`/api/cloud/connections/${connectionId}/config`)
}
