import { defineStore } from 'pinia'
import { ref } from 'vue'

import { useApi } from '../composables/useApi.js'

export const useAdminStore = defineStore('admin', () => {
  const api = useApi()
  const lastError = ref(null)

  // Every admin action runs through _guard: reset lastError, run the call,
  // surface its message to lastError on failure, and re-throw so callers still
  // see the rejection. One copy of the capture/rethrow instead of 13.
  async function _guard(fn) {
    lastError.value = null
    try {
      return await fn()
    } catch (e) {
      lastError.value = e.message
      throw e
    }
  }

  // The Tier-A maintenance endpoints share one shape: POST a dry_run flag
  // (default the SAFE preview), optionally with extra body fields. The backend's
  // same predicate drives preview + apply, so the UI just toggles dryRun.
  function _dryRunPost(url, { dryRun = true, ...extra } = {}) {
    return _guard(() => api.post(url, { body: { dry_run: dryRun, ...extra } }))
  }

  // --- Tier-C: artist cascade ---------------------------------------

  function projectArtistCascade(slug) {
    return _guard(() => api.post(
      `/api/admin/artists/${encodeURIComponent(slug)}/cascade-delete`,
      { body: { dry_run: true } },
    ))
  }

  function dispatchArtistCascade(slug, confirm) {
    return _guard(() => api.post(
      `/api/admin/artists/${encodeURIComponent(slug)}/cascade-delete`,
      { body: { dry_run: false, confirm } },
    ))
  }

  // --- Tier-C: bulk image delete ------------------------------------

  function projectBulkImageDelete(imageIds) {
    return _guard(() => api.post(
      '/api/admin/images/bulk-delete',
      { body: { image_ids: imageIds, dry_run: true } },
    ))
  }

  function dispatchBulkImageDelete(imageIds, confirm) {
    return _guard(() => api.post(
      '/api/admin/images/bulk-delete',
      { body: { image_ids: imageIds, dry_run: false, confirm } },
    ))
  }

  // --- Tier-B: tag delete + merge -----------------------------------

  function deleteTag(tagId) {
    return _guard(() => api.delete(`/api/admin/tags/${tagId}`))
  }

  // dryRun → {preview: {...}} (non-mutating counts + sample); else {result}.
  function mergeTags(destId, sourceId, { dryRun = false } = {}) {
    return _guard(() => api.post(
      `/api/admin/tags/${destId}/merge`,
      { body: { source_id: sourceId, dry_run: dryRun } },
    ))
  }

  async function tagUsageCount(tagId) {
    return _guard(async () => {
      const body = await api.get(`/api/admin/tags/${tagId}/usage-count`)
      return body.count
    })
  }

  // --- Tier-A: dry-run/apply maintenance ----------------------------

  function pruneUnusedTags(opts = {}) {
    return _dryRunPost('/api/admin/tags/prune-unused', opts)
  }

  // Delete bare posts (no images + no attachments) — the empty-post flood
  // shells. Preview/apply parity on the backend; UI previews then confirms.
  function pruneBarePosts(opts = {}) {
    return _dryRunPost('/api/admin/posts/prune-bare', opts)
  }

  // Unify duplicate post rows (gallery-dl attachment-id + native post-id for the
  // same real post) onto one post-id-keyed keeper. Images untouched. sourceId
  // (optional) scopes to one source; sent as source_id in the body.
  function reconcileDuplicatePosts({ sourceId = null, ...opts } = {}) {
    return _dryRunPost('/api/admin/posts/reconcile-duplicates', {
      ...opts, source_id: sourceId,
    })
  }

  // Destructive whole-instance reset: deletes ALL general + character tags AND
  // their applications (the heads' training data included) — fandom + series
  // preserved. dry-run returns a `confirm` token; the apply must pass it back
  // ({ dryRun: false, confirm }) or the server rejects it.
  function resetContentTagging(opts = {}) {
    return _dryRunPost('/api/admin/tags/reset-content', opts)
  }

  // #714: Title-Case the back-catalog + merge case/whitespace-variant tags.
  // dry-run returns a projection inline; live returns {task_id} (long op —
  // FK repoints) the caller polls via pollTaskUntilDone.
  function normalizeTags(opts = {}) {
    return _dryRunPost('/api/admin/tags/normalize', opts)
  }

  // --- Task progress polling (taps FC-3i activity dashboard) --------

  /**
   * Polls /api/system/activity/runs?queue=maintenance every 3s,
   * resolves when a task_run row with the given celery task_id
   * reaches a terminal status (ok / error / timeout). Returns the
   * row. Times out after 30 min by default.
   */
  async function pollTaskUntilDone(taskId, { timeoutMs = 1_800_000 } = {}) {
    const deadline = Date.now() + timeoutMs
    while (Date.now() < deadline) {
      const body = await api.get(
        '/api/system/activity/runs',
        { params: { queue: 'maintenance', limit: 20 } },
      )
      const row = (body.runs || []).find(r => r.celery_task_id === taskId)
      if (row && ['ok', 'error', 'timeout'].includes(row.status)) {
        return row
      }
      await new Promise((r) => setTimeout(r, 3000))
    }
    throw new Error(`task ${taskId} did not finish within ${timeoutMs}ms`)
  }

  return {
    lastError,
    projectArtistCascade,
    dispatchArtistCascade,
    projectBulkImageDelete,
    dispatchBulkImageDelete,
    deleteTag,
    mergeTags,
    tagUsageCount,
    pruneUnusedTags,
    pruneBarePosts,
    reconcileDuplicatePosts,
    resetContentTagging,
    normalizeTags,
    pollTaskUntilDone,
  }
})
