<template>
  <MaintenanceTile
    icon="mdi-translate"
    title="Post translation (Interpreter)"
    blurb="Translate non-English post titles + descriptions to English via your self-hosted Interpreter service, so archived posts read naturally."
  >
    <div class="d-flex align-center mb-3" style="gap: 10px;">
      <span class="fc-section-h">Enabled</span>
      <v-switch
        v-model="enabled" :loading="busy" hide-details density="compact"
        color="success" class="ml-auto" @update:model-value="onToggle"
      />
    </div>
    <p class="fc-muted text-body-2 mb-3">
      Point this at your Interpreter service. It stays off until a URL is set, and
      only translates posts whose text isn't already in the target language — the
      English is shown by default on post cards, with a toggle back to the original.
    </p>

    <v-text-field
      v-model="baseUrl" label="Interpreter base URL"
      placeholder="http://interpreter.your-domain/" density="compact"
      hide-details class="mb-3" :disabled="busy" @change="onSave"
    />
    <v-text-field
      v-model="targetLang" label="Target language" density="compact"
      hide-details style="max-width: 160px;" class="mb-3" :disabled="busy"
      @change="onSave"
    />

    <!-- Acceptance floor (#155): latin-script translations below this Interpreter
         confidence are kept as the original. Per-post overrides handle exceptions. -->
    <v-text-field
      v-model.number="minConfidence" label="Acceptance confidence"
      type="number" min="0" max="1" step="0.01" density="compact"
      hide-details style="max-width: 200px;" class="mb-1" :disabled="busy"
      @change="onSaveConfidence"
    />
    <div class="fc-muted text-caption mb-3">
      Latin-script titles Interpreter is less than this sure about (0–1) are kept
      as the original; CJK is always translated. Default 0.90 — raise it to reject
      more mis-detections, lower it to translate more. Per-post controls in the
      posts feed override this either way.
    </div>

    <div class="d-flex align-center flex-wrap mb-3" style="gap: 8px;">
      <v-icon size="12" :color="statusColor">mdi-circle</v-icon>
      <span class="fc-muted text-body-2">{{ statusText }}</span>
      <v-btn
        size="x-small" variant="tonal" rounded="pill" class="ml-2"
        :loading="testing" :disabled="!baseUrl" @click="onTest"
      >Test connection</v-btn>
      <span
        v-if="testResult !== null" class="text-caption"
        :class="testResult ? 'text-success' : 'text-error'"
      >{{ testResult ? '✓ reachable' : '✗ unreachable' }}</span>
    </div>

    <div class="d-flex align-center flex-wrap" style="gap: 10px;">
      <v-btn
        size="small" variant="flat" color="accent" rounded="pill"
        prepend-icon="mdi-translate" :loading="running"
        :disabled="!enabled || !baseUrl" @click="onRun"
      >Translate now</v-btn>
      <v-btn
        size="small" variant="tonal" rounded="pill"
        prepend-icon="mdi-refresh" :loading="retranslating"
        :disabled="!enabled || !baseUrl" @click="confirmAll = true"
      >Re-translate all</v-btn>
      <span
        v-if="status && status.active"
        class="fc-muted text-caption d-inline-flex align-center" style="gap: 6px;"
      >
        <v-progress-circular indeterminate size="12" width="2" color="accent" />
        Translating… {{ status.untranslated_count }} remaining
      </span>
      <span v-else-if="status" class="fc-muted text-caption">
        {{ status.untranslated_count }}
        post{{ status.untranslated_count === 1 ? '' : 's' }} awaiting translation
      </span>
    </div>
    <p
      v-if="status && status.last_run && ['error', 'timeout'].includes(status.last_run.status)"
      class="text-caption text-error mt-1 mb-0"
    >
      Last translation run ended with “{{ status.last_run.status }}”. It resumes on
      the next sweep; check the Interpreter connection if it persists.
    </p>
    <p class="fc-muted text-caption mt-2 mb-0">
      Use <strong>Re-translate all</strong> after switching the Interpreter model —
      it clears every stored translation and re-runs it through the new model
      (unchanged text is served from cache, so it's cheap). To refresh just one
      artist, use the Re-translate button on that artist's page.
    </p>

    <v-divider class="my-3" />

    <!-- Diagnostic probe (task #1376): paste text → what Interpreter DETECTS
         (language + confidence) and returns, without saving. Used to see why a
         short/abbreviation-heavy English title gets mis-detected, and to pick a
         detection-confidence / min-length guard from real numbers. -->
    <div class="mb-1">
      <span class="fc-section-h">Test translation</span>
      <p class="fc-muted text-caption mt-1 mb-2">
        Paste a title or snippet to see what Interpreter detects and returns —
        handy for checking why a short string is mis-detected. Nothing is saved.
      </p>
    </div>
    <v-textarea
      v-model="probeText" label="Text to test" rows="2" auto-grow
      density="compact" hide-details class="mb-2" :disabled="!baseUrl"
    />
    <div class="d-flex align-center flex-wrap mb-2" style="gap: 8px;">
      <v-btn
        size="small" variant="tonal" rounded="pill" prepend-icon="mdi-magnify"
        :loading="probing" :disabled="!baseUrl || !probeText.trim()"
        @click="onProbe"
      >Test translation</v-btn>
    </div>
    <v-sheet
      v-if="probeResult" rounded class="pa-3 mb-1 text-body-2"
      color="rgba(var(--v-theme-accent), 0.06)"
    >
      <div class="d-flex flex-wrap" style="gap: 4px 16px;">
        <span>
          <span class="fc-muted">Detected:</span>
          <strong>{{ probeResult.detected_lang || '—' }}</strong>
        </span>
        <span v-if="probeResult.detected_confidence != null">
          <span class="fc-muted">Confidence:</span>
          <strong>{{ fmtConf(probeResult.detected_confidence) }}</strong>
        </span>
        <span>
          <span class="fc-muted">Engine:</span>
          {{ probeResult.engine || '—' }}<template
            v-if="probeResult.engine_version"
          > ({{ probeResult.engine_version }})</template>
        </span>
        <span><span class="fc-muted">Target:</span> {{ probeResult.target }}</span>
      </div>
      <v-divider class="my-2" />
      <div class="fc-muted mb-1">
        Result<template v-if="probeResult.detected_lang === probeResult.target">
          — already {{ probeResult.target }}, so the sweep leaves it as-is</template>:
      </div>
      <p class="mb-0" style="white-space: pre-wrap;">{{
        probeResult.translated || '(no change)'
      }}</p>
    </v-sheet>

    <v-alert
      v-if="err" type="error" variant="tonal" density="compact"
      class="mt-3" closable @click:close="err = null"
    >{{ err }}</v-alert>

    <!-- m146: re-translate-everything is a bigger hammer than 'Translate now'
         (it clears + rebuilds all translations), so it asks first. -->
    <v-dialog v-model="confirmAll" max-width="440">
      <v-card>
        <v-card-title>Re-translate everything?</v-card-title>
        <v-card-text class="text-body-2">
          This clears the stored translation for <strong>every</strong> post and
          re-runs them through your current Interpreter model. Text that hasn't
          changed comes back from the cache instantly; anything the new model
          translates differently is refreshed. It runs in the background until
          done.
        </v-card-text>
        <v-card-actions>
          <v-spacer />
          <v-btn variant="text" @click="confirmAll = false">Cancel</v-btn>
          <v-btn
            color="accent" :loading="retranslating" @click="onRetranslateAll"
          >Re-translate all</v-btn>
        </v-card-actions>
      </v-card>
    </v-dialog>
  </MaintenanceTile>
</template>

<script setup>
import { computed, onMounted, onUnmounted, ref } from 'vue'
import { toast } from '../../utils/toast.js'
import { useApi } from '../../composables/useApi.js'
import { useImportStore } from '../../stores/import.js'
import MaintenanceTile from '../common/MaintenanceTile.vue'

const api = useApi()
const store = useImportStore()

const enabled = ref(false)
const baseUrl = ref('')
const targetLang = ref('en')
const minConfidence = ref(0.9)
const busy = ref(false)
const running = ref(false)
const retranslating = ref(false)
const confirmAll = ref(false)
const testing = ref(false)
const testResult = ref(null)   // null = not tested this session; true/false = last result
const status = ref(null)
const err = ref(null)

// #1376 "Test translation" probe: paste text → detected lang/confidence + result.
const probeText = ref('')
const probing = ref(false)
const probeResult = ref(null)

// Confidence scale is Interpreter-defined (0–100 or 0–1) — show it as-is, just
// trimmed to 2 decimals, so the operator reads the true number.
function fmtConf (c) {
  return c == null ? '' : Math.round(c * 100) / 100
}

const statusColor = computed(() => {
  if (!enabled.value || !baseUrl.value) return 'grey'
  return status.value?.healthy ? 'success' : 'error'
})
const statusText = computed(() => {
  if (!enabled.value) return 'Off'
  if (!baseUrl.value) return 'No base URL set'
  if (status.value == null) return 'Checking…'
  return status.value.healthy ? 'Interpreter reachable' : 'Interpreter unreachable'
})

let pollTimer = null
async function loadStatus() {
  try { status.value = await api.get('/api/settings/translation/status') }
  catch { status.value = null }
  // Poll live while a sweep is running so the remaining count ticks down; stop
  // when idle (a fresh trigger restarts it via its own setTimeout(loadStatus)).
  clearTimeout(pollTimer)
  if (status.value?.active) pollTimer = setTimeout(loadStatus, 3000)
}
onUnmounted(() => clearTimeout(pollTimer))

async function onTest() {
  // Ping /v1/health for the CURRENTLY-typed URL (not the saved one), so a new
  // URL can be verified before enabling. Also reflects in the status dot.
  testing.value = true
  err.value = null
  testResult.value = null
  try {
    const r = await api.post('/api/settings/translation/test', {
      body: { base_url: baseUrl.value.trim() },
    })
    testResult.value = !!r.healthy
    if (status.value) status.value.healthy = !!r.healthy
  } catch (e) {
    err.value = e.message
  } finally {
    testing.value = false
  }
}

async function onProbe () {
  probing.value = true
  err.value = null
  probeResult.value = null
  try {
    probeResult.value = await api.post('/api/settings/translation/probe', {
      body: { text: probeText.value.trim() },
    })
  } catch (e) {
    err.value = e.message
  } finally {
    probing.value = false
  }
}

onMounted(async () => {
  try {
    await store.loadSettings()
    const s = store.settings || {}
    enabled.value = !!s.translation_enabled
    baseUrl.value = s.interpreter_base_url || ''
    targetLang.value = s.translation_target_lang || 'en'
    minConfidence.value = s.translation_min_confidence ?? 0.9
  } catch { /* non-fatal */ }
  await loadStatus()
})

async function save(patch) {
  busy.value = true
  err.value = null
  try { await store.patchSettings(patch) }
  catch (e) { err.value = e.message }
  finally { busy.value = false }
}

async function onToggle(val) {
  await save({ translation_enabled: !!val })
  toast({ text: val ? 'Translation on' : 'Translation off', type: 'success' })
  await loadStatus()
}
async function onSave() {
  await save({
    interpreter_base_url: baseUrl.value.trim(),
    translation_target_lang: (targetLang.value || 'en').trim() || 'en',
  })
  await loadStatus()
}
async function onSaveConfidence() {
  // Clamp to [0, 1] so a stray value never bounces off the API 400.
  let v = Number(minConfidence.value)
  if (!Number.isFinite(v)) v = 0.9
  v = Math.min(1, Math.max(0, v))
  minConfidence.value = v
  await save({ translation_min_confidence: v })
}
async function onRun() {
  running.value = true
  err.value = null
  try {
    await api.post('/api/settings/translation/run')
    toast({ text: 'Translation sweep started', type: 'success' })
  } catch (e) {
    err.value = e.message
  } finally {
    running.value = false
    setTimeout(loadStatus, 1500)   // let the sweep make a dent, then refresh
  }
}
async function onRetranslateAll() {
  retranslating.value = true
  err.value = null
  try {
    await api.post('/api/settings/translation/retranslate', { body: { all: true } })
    confirmAll.value = false
    toast({ text: 'Re-translating all posts…', type: 'success' })
  } catch (e) {
    err.value = e.message
  } finally {
    retranslating.value = false
    setTimeout(loadStatus, 1500)   // reset spikes the untranslated count — refresh it
  }
}
</script>
