<template>
  <div v-if="visible" class="vm-data-i18n-btn">
    <button
      type="button"
      class="vm-crud-toolbar__btn is-ghost"
      :disabled="disabled || submitting"
      @click="openDialog"
    >
      <i class="ri-translate-2" />
      {{ text || crudLabel('dataI18nTranslate', crud.dict.label) }}
    </button>

    <Dialog :open="open" @update:open="onOpenChange">
      <DialogContent
        shell="crud"
        :show-close-button="false"
        class="vm-data-i18n-btn__dialog"
        @pointer-down-outside="preventDialogOutsideClose"
        @focus-outside="preventDialogOutsideClose"
        @interact-outside="preventDialogOutsideClose"
      >
        <DialogHeader class="vm-crud-shell__head">
          <DialogTitle class="vm-crud-shell__title">
            {{ crudLabel('dataI18nTranslateTitle', crud.dict.label) }}
          </DialogTitle>
          <DialogClose class="vm-crud-shell__close">
            <i class="ri-close-line" />
            <span class="sr-only">Close</span>
          </DialogClose>
        </DialogHeader>

        <!-- 与 vm-upsert 同壳：中间 body flex:1 吃空白，footer 固定高度贴底 -->
        <div class="vm-crud-shell__body vm-data-i18n-btn__body">
          <div
            class="vm-data-i18n-btn__field"
            :style="{ gridColumn: `span ${fieldSpan}` }"
          >
            <Label class="vm-data-i18n-btn__label">
              {{ crudLabel('dataI18nTable', crud.dict.label) }}
            </Label>
            <Input
              class="vm-data-i18n-btn__control is-readonly"
              :model-value="tableName"
              disabled
            />
          </div>

          <div
            class="vm-data-i18n-btn__field"
            :style="{ gridColumn: `span ${fieldSpan}` }"
          >
            <Label class="vm-data-i18n-btn__label">
              {{ crudLabel('dataI18nLang', crud.dict.label) }}
              <span class="vm-data-i18n-btn__req">*</span>
            </Label>
            <vm-select
              v-model="form.langCode"
              class="vm-data-i18n-btn__control is-select"
              :options="langOptions"
              :refresh-on-change="false"
              width="100%"
              :placeholder="crudLabel('dataI18nPickLang', crud.dict.label)"
            />
          </div>

          <div
            class="vm-data-i18n-btn__field"
            :style="{ gridColumn: `span ${fieldSpan}` }"
          >
            <Label class="vm-data-i18n-btn__label">
              {{ crudLabel('dataI18nModel', crud.dict.label) }}
              <span class="vm-data-i18n-btn__req">*</span>
            </Label>
            <vm-select
              v-model="form.model"
              class="vm-data-i18n-btn__control is-select"
              :options="modelOptions"
              :refresh-on-change="false"
              width="100%"
              :placeholder="crudLabel('dataI18nPickModel', crud.dict.label)"
            />
          </div>

          <div
            class="vm-data-i18n-btn__field"
            :style="{ gridColumn: `span ${fieldSpan}` }"
          >
            <Label class="vm-data-i18n-btn__label">
              {{ crudLabel('dataI18nMode', crud.dict.label) }}
              <span class="vm-data-i18n-btn__req">*</span>
            </Label>
            <vm-select
              v-model="form.mode"
              class="vm-data-i18n-btn__control is-select"
              :options="modeOptions"
              :refresh-on-change="false"
              width="100%"
            />
          </div>
        </div>

        <DialogFooter class="vm-crud-shell__foot">
          <button
            type="button"
            class="vm-crud-shell__btn is-ghost"
            :disabled="submitting"
            @click="open = false"
          >
            {{ crudLabel('close', crud.dict.label) }}
          </button>
          <button
            type="button"
            class="vm-crud-shell__btn is-primary"
            :disabled="submitting || !form.model || !form.langCode"
            @click="runTranslate"
          >
            {{
              submitting
                ? crudLabel('dataI18nTranslating', crud.dict.label)
                : crudLabel('dataI18nTranslate', crud.dict.label)
            }}
          </button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  </div>
</template>

<script setup lang="ts">
import { computed, reactive, ref, watch } from 'vue'
import { toast } from 'vue-sonner'
import { isAiStreamResult } from '../api/client'
import { preventDialogOutsideClose } from '../lib/dialog-float'
import { useVome } from '../hooks/useVome'
import { useCrud } from './useCrud'
import VmSelect from './components/vm-select.vue'
import { resolveFormSpan, useFormCols } from './span'
import { crudFormat, crudLabel } from './style'
import {
  canUseDataI18n,
  dataPackFromService,
  loadEnabledLangOptions,
} from './data-i18n'

defineOptions({ name: 'vm-data-i18n-btn' })

const props = withDefaults(
  defineProps<{
    disabled?: boolean
    text?: string
    tableName?: string
  }>(),
  {
    disabled: false,
  },
)

const crud = useCrud()
const { service } = useVome()
const open = ref(false)
const submitting = ref(false)
const langOptions = ref<Array<{ label: string; value: string }>>([])
const modelOptions = ref<Array<{ label: string; value: string }>>([])
const formCols = useFormCols()
const fieldSpan = computed(() => resolveFormSpan(12, formCols.value))

const form = reactive({
  langCode: 'en-US',
  model: '',
  mode: 'incremental' as 'incremental' | 'full',
})

const modeOptions = computed(() => [
  {
    label: crudLabel('dataI18nModeIncremental', crud.dict.label),
    value: 'incremental',
  },
  {
    label: crudLabel('dataI18nModeFull', crud.dict.label),
    value: 'full',
  },
])

const meta = computed(() =>
  canUseDataI18n(crud.service, service, props.tableName),
)

const tableName = computed(() => meta.value.tableName)
const visible = computed(() => meta.value.ok)

watch(open, (v) => {
  if (v) void loadDialogData()
})

function onOpenChange(v: boolean) {
  open.value = v
}

function openDialog() {
  if (props.disabled || submitting.value) return
  open.value = true
}

async function loadDialogData() {
  await Promise.all([loadLangs(), loadModels()])
  const viewing = crud.dataI18nLangCode?.value
  if (viewing && langOptions.value.some((o) => o.value === viewing)) {
    form.langCode = viewing
  }
}

async function loadLangs() {
  const rows = await loadEnabledLangOptions(service)
  langOptions.value = rows.map((r) => ({ label: r.label, value: r.value }))
  if (!langOptions.value.some((o) => o.value === form.langCode)) {
    form.langCode = langOptions.value[0]?.value || 'en-US'
  }
}

async function loadModels() {
  const dataPack = dataPackFromService(service)
  if (!dataPack?.chatModels) {
    modelOptions.value = []
    form.model = ''
    toast.error(crudLabel('noChatModels'))
    return
  }
  try {
    const list = (await dataPack.chatModels()) as Array<{ code?: string }>
    modelOptions.value = (list || [])
      .filter((m) => m.code)
      .map((m) => ({ label: String(m.code), value: String(m.code) }))
    if (!modelOptions.value.some((o) => o.value === form.model)) {
      form.model = modelOptions.value[0]?.value || ''
    }
  } catch {
    modelOptions.value = []
    form.model = ''
  }
}

async function runTranslate() {
  if (submitting.value || props.disabled) return
  const table = tableName.value
  if (!table) {
    toast.error(crudLabel('dataI18nNoTable', crud.dict.label))
    return
  }
  if (!form.langCode) {
    toast.error(crudLabel('dataI18nNeedLang', crud.dict.label))
    return
  }
  if (!form.model) {
    toast.error(crudLabel('dataI18nNeedModel', crud.dict.label))
    return
  }
  const dataPack = dataPackFromService(service)
  if (!dataPack?.translateTable) {
    toast.error(crudLabel('dataI18nNoService', crud.dict.label))
    return
  }
  submitting.value = true
  try {
    const langName =
      langOptions.value.find((o) => o.value === form.langCode)?.label ||
      form.langCode
    const out = await dataPack.translateTable({
      tableName: table,
      langCode: form.langCode,
      langName,
      mode: form.mode,
      model: form.model,
    })
    let skipped = false
    let message = ''
    let rowCount = 0
    if (isAiStreamResult(out)) {
      for await (const chunk of out.stream) {
        if (chunk.type === 'error') {
          throw new Error(
            chunk.error?.message || crudLabel('dataI18nFail', crud.dict.label),
          )
        }
        if (chunk.type === 'done') {
          skipped = Boolean(chunk.data?.skipped)
          message = String(chunk.data?.message || '')
          rowCount = Number(chunk.data?.rowCount ?? 0)
        }
      }
    } else {
      const res = out as {
        skipped?: boolean
        message?: string
        rowCount?: number
      }
      skipped = Boolean(res?.skipped)
      message = String(res?.message || '')
      rowCount = Number(res?.rowCount ?? 0)
    }
    if (skipped) {
      toast.message(message || crudLabel('dataI18nSkipped', crud.dict.label))
    } else {
      toast.success(
        crudFormat(crudLabel('dataI18nDone', crud.dict.label), rowCount),
      )
    }
    open.value = false
    if (crud.dataI18nLangCode) {
      crud.dataI18nLangCode.value = form.langCode
    }
    if (crud.trashMode.value !== 'dataI18n') {
      crud.trashMode.value = 'dataI18n'
    }
    await crud.refresh()
  } catch (e) {
    toast.error(
      e instanceof Error
        ? e.message
        : crudLabel('dataI18nFail', crud.dict.label),
    )
  } finally {
    submitting.value = false
  }
}
</script>

<style lang="scss" scoped>
.vm-data-i18n-btn {
  display: inline-flex;
}

.vm-crud-toolbar__btn {
  display: inline-flex;
  height: 36px;
  align-items: center;
  gap: 4px;
  padding: 0 14px;
  border: none;
  border-radius: 12px;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  background: var(--muted);
  color: var(--foreground);
  transition: background 0.15s ease, opacity 0.15s ease;

  &:disabled {
    cursor: not-allowed;
    opacity: 0.45;
  }

  &:hover:not(:disabled) {
    background: color-mix(in srgb, var(--muted) 70%, var(--foreground) 8%);
  }
}

.vm-data-i18n-btn__body {
  /* 覆盖 shell body 的 flex-column，改成表单网格；仍继承 flex:1 占满中间空白 */
  display: grid;
  grid-template-columns: repeat(24, minmax(0, 1fr));
  gap: 12px 16px;
  align-content: start;
  padding: 18px 22px 16px;

  @media (max-width: 768px) {
    grid-template-columns: repeat(12, minmax(0, 1fr));
    gap: 10px;
    padding: 12px;
  }
}

.vm-data-i18n-btn__label {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  margin-bottom: 6px;
  font-size: 12px;
  color: var(--muted-foreground);
}

.vm-data-i18n-btn__req {
  color: var(--destructive);
}

.vm-data-i18n-btn__field {
  min-width: 0;
}

.vm-data-i18n-btn__control {
  width: 100%;
  height: var(--vm-control-height) !important;
  min-height: var(--vm-control-height);
  border-radius: var(--vm-control-radius) !important;
  font-size: var(--vm-control-font-size) !important;

  &.is-readonly {
    background: var(--muted) !important;
    color: var(--muted-foreground) !important;
    opacity: 1 !important;
    cursor: default;
  }

  &.is-select {
    background: var(--muted) !important;
  }
}
</style>
