<script setup lang="ts">
import { NInput, NSelect } from 'naive-ui'
import { computed, nextTick, reactive, ref, watch } from 'vue'

import { listCachedCompartments, listCachedRegions, listConfigs } from '@/api/configs'
import { createTask, updateTask } from '@/api/tasks'
import AppInputNumber from '@/components/AppInputNumber.vue'
import FormField from '@/components/FormField.vue'
import FormModal from '@/components/FormModal.vue'
import FormSection from '@/components/FormSection.vue'
import InstanceSpecForm from '@/components/instance/InstanceSpecForm.vue'
import CronEditor from '@/components/task/CronEditor.vue'
import TaskTypeIcon from '@/components/task/TaskTypeIcon.vue'
import TenantPicker from '@/components/TenantPicker.vue'
import { defaultResourceName } from '@/composables/useFormat'
import { useAsync } from '@/composables/useAsync'
import type { Task, TaskType } from '@/types/api'
import { useToast } from '@/composables/useToast'

/** task 传入即为编辑模式：回填全部字段，任务类型锁定不可切换 */
const props = defineProps<{ show: boolean; task?: Task | null }>()
const emit = defineEmits<{ 'update:show': [boolean]; created: [] }>()

const isEdit = computed(() => !!props.task)

/** 任务类型选择卡:新建时三选一,编辑时锁定当前类型 */
const TYPE_CARDS: { value: TaskType; title: string; desc: string }[] = [
  { value: 'health_check', title: '定时测活', desc: '周期性验证租户凭据,失效推送通知' },
  { value: 'cost', title: '每日成本同步', desc: '拉取 Usage 账单汇总到总览' },
  { value: 'snatch', title: '抢机', desc: '容量不足时反复尝试创建,抢满自停' },
]

const message = useToast()
const submitting = ref(false)
const specForm = ref<InstanceType<typeof InstanceSpecForm> | null>(null)

const blank = {
  type: 'health_check' as TaskType,
  name: '',
  cronExpr: '',
  checkCfgIds: [] as number[],
  snatchCfgId: null as number | null,
  snatchRegion: '',
  snatchCompartmentId: '',
  count: 1,
  displayName: '',
}
const form = reactive({ ...blank })

/** 编辑回填期间抑制 snatchCfgId watch 的级联清空 */
let applying = false

watch(
  () => props.show,
  async (v) => {
    if (!v) return
    if (!props.task) {
      Object.assign(form, blank, { checkCfgIds: [], displayName: defaultResourceName('snatch') })
      return
    }
    const t = props.task
    const p = JSON.parse(t.payload || '{}') as Record<string, unknown>
    applying = true
    Object.assign(form, blank, {
      type: t.type,
      name: t.name,
      cronExpr: t.cronExpr,
      checkCfgIds: Array.isArray(p.ociConfigIds) ? p.ociConfigIds : [],
    })
    if (t.type === 'snatch') {
      const inst = (p.instance ?? {}) as Record<string, unknown>
      form.snatchCfgId = typeof p.ociConfigId === 'number' ? p.ociConfigId : null
      form.count = typeof p.count === 'number' ? p.count : 1
      form.displayName = String(inst.displayName ?? '')
      form.snatchRegion = typeof inst.region === 'string' ? inst.region : ''
      form.snatchCompartmentId = typeof inst.compartmentId === 'string' ? inst.compartmentId : ''
      await nextTick() // 等 snatch tab 与 SpecForm 挂载
      await nextTick()
      specForm.value?.applySpec(inst)
    }
    await nextTick()
    applying = false
  },
)

const configs = useAsync(listConfigs)

const typeLabel = computed(
  () => TYPE_CARDS.find((c) => c.value === form.type)?.title ?? form.type,
)

/** 类型卡点击:编辑态锁定;切换时保持已填的名称与计划 */
function pickType(t: TaskType) {
  if (isEdit.value || form.type === t) return
  form.type = t
}
const snatchCfg = computed(
  () => configs.data.value?.find((c) => c.id === form.snatchCfgId) ?? null,
)

/** 抢机目标租户的区域 / 区间数据源（与全局作用域选择器同一套缓存接口） */
const regions = useAsync(
  () =>
    form.snatchCfgId === null
      ? Promise.resolve([])
      : listCachedRegions(form.snatchCfgId).catch(() => []),
  false,
)
const compartments = useAsync(
  () =>
    form.snatchCfgId === null
      ? Promise.resolve([])
      : listCachedCompartments(form.snatchCfgId).catch(() => []),
  false,
)

watch(
  () => form.snatchCfgId,
  () => {
    if (!applying) {
      form.snatchRegion = ''
      form.snatchCompartmentId = ''
    }
    void regions.run()
    void compartments.run()
  },
)

// 区域加载后默认主区域；未开启多区域的租户锁定唯一默认区域
watch(
  () => regions.data.value,
  (subs) => {
    if (!subs?.length) return
    if (!form.snatchRegion || !subs.some((s) => s.name === form.snatchRegion))
      form.snatchRegion = subs.find((s) => s.isHomeRegion)?.name ?? subs[0].name
  },
)

const regionDisabled = computed(() => !snatchCfg.value?.multiRegion)
const compDisabled = computed(() => !snatchCfg.value?.multiCompartment)

const regionOptions = computed(() =>
  (regions.data.value ?? []).map((s) => ({
    label: s.isHomeRegion ? `${s.alias} · 主` : s.alias,
    value: s.name,
  })),
)
const compOptions = computed(() => [
  { label: `${snatchCfg.value?.tenancyName || '租户'}（根）`, value: '' },
  ...(compartments.data.value ?? []).map((c) => ({ label: c.name, value: c.id })),
])

const canSubmit = computed(() => {
  if (!form.name.trim() || !form.cronExpr.trim()) return false
  if (form.type !== 'snatch') return true
  if (form.snatchCfgId === null || !form.displayName.trim()) return false
  return specForm.value?.valid ?? false
})

function buildPayload(): Record<string, unknown> {
  if (form.type !== 'snatch') return { ociConfigIds: form.checkCfgIds }
  return {
    ociConfigId: form.snatchCfgId,
    count: form.count,
    instance: {
      displayName: form.displayName.trim(),
      region: form.snatchRegion || undefined,
      compartmentId: form.snatchCompartmentId || undefined,
      ...(specForm.value?.buildSpec() ?? {}),
    },
  }
}

async function submit() {
  submitting.value = true
  try {
    if (props.task) {
      await updateTask(props.task.id, {
        name: form.name.trim(),
        cronExpr: form.cronExpr.trim(),
        payload: buildPayload(),
      })
      message.success(`任务「${form.name.trim()}」已更新`)
    } else {
      const created = await createTask({
        name: form.name.trim(),
        type: form.type,
        cronExpr: form.cronExpr.trim(),
        payload: buildPayload(),
      })
      message.success(`任务「${created.name}」已创建并进入调度`)
    }
    emit('update:show', false)
    emit('created')
  } catch (e) {
    message.error(e instanceof Error ? e.message : isEdit.value ? '更新失败' : '创建失败')
  } finally {
    submitting.value = false
  }
}
</script>

<template>
  <FormModal
    :show="show"
    :title="isEdit ? '编辑后台任务' : '新建后台任务'"
    :width="600"
    :submitting="submitting"
    :submit-disabled="!canSubmit"
    :submit-text="isEdit ? '保存' : '创建并调度'"
    @update:show="emit('update:show', $event)"
    @submit="submit"
  >
    <template v-if="isEdit" #header>
      <div class="flex items-center gap-2.5">
        <span>编辑后台任务</span>
        <span
          class="flex items-center gap-1 rounded-full border border-line bg-wash px-2.5 py-0.5 text-[11px] font-medium text-ink-2"
        >
          <svg class="h-2.5 w-2.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
            <rect x="3" y="11" width="18" height="11" rx="2" />
            <path d="M7 11V7a5 5 0 0 1 10 0v4" />
          </svg>
          {{ typeLabel }} · 类型不可改
        </span>
      </div>
    </template>

    <FormField v-if="!isEdit" label="任务类型" required>
      <div class="grid grid-cols-3 gap-2.5 max-md:grid-cols-1">
        <button
          v-for="c in TYPE_CARDS"
          :key="c.value"
          type="button"
          class="cursor-pointer rounded-lg border bg-white p-3 text-left"
          :class="
            form.type === c.value
              ? 'border-accent shadow-[0_0_0_1px_var(--color-accent)]'
              : 'border-line hover:border-ink-3'
          "
          @click="pickType(c.value)"
        >
          <span
            class="mb-2 flex h-7 w-7 items-center justify-center rounded-md"
            :class="form.type === c.value ? 'bg-accent/12 text-accent' : 'bg-wash text-ink-2'"
          >
            <TaskTypeIcon :type="c.value" class="h-3.5 w-3.5" />
          </span>
          <div class="text-[12.5px] font-semibold">{{ c.title }}</div>
          <div class="mt-0.5 text-[11px] leading-snug text-ink-3">{{ c.desc }}</div>
        </button>
      </div>
    </FormField>

    <FormField label="任务名" required>
      <NInput v-model:value="form.name" placeholder="如「每日全量测活」" />
    </FormField>
    <FormField
      label="执行计划"
      required
      hint="按服务器本地时区调度；选「自定义 cron」可直接书写 5 段表达式"
    >
      <CronEditor v-model:value="form.cronExpr" />
    </FormField>

    <template v-if="form.type === 'health_check'">
      <FormField label="测活范围" hint="留空表示全部租户;搜索同时匹配别名与租户名称">
        <TenantPicker
          v-model:values="form.checkCfgIds"
          multiple
          clearable
          :configs="configs.data.value ?? []"
          :loading="configs.loading.value"
          placeholder="全部租户"
        />
      </FormField>
    </template>

    <template v-else-if="form.type === 'cost'">
      <FormField
        label="同步范围"
        hint="留空表示全部租户；免费类别租户执行时自动跳过，不发起 Usage API 请求"
      >
        <TenantPicker
          v-model:values="form.checkCfgIds"
          multiple
          clearable
          :configs="configs.data.value ?? []"
          :loading="configs.loading.value"
          placeholder="全部租户"
        />
      </FormField>
    </template>

    <template v-else>
      <FormSection title="目标" />
      <div class="grid grid-cols-2 gap-3 max-md:grid-cols-1">
        <FormField label="目标租户" required>
          <TenantPicker
            v-model:value="form.snatchCfgId"
            :configs="configs.data.value ?? []"
            :loading="configs.loading.value"
          />
        </FormField>
        <FormField label="目标台数" hint="每次调度按剩余台数尝试,抢满后任务自动停止">
          <AppInputNumber v-model:value="form.count" :min="1" :max="10" class="w-full" />
        </FormField>
      </div>
      <div class="grid grid-cols-2 gap-3 max-md:grid-cols-1">
        <FormField label="区域" hint="未开启多区域的租户锁定默认区域">
          <NSelect
            v-model:value="form.snatchRegion"
            :options="regionOptions"
            :loading="regions.loading.value"
            :disabled="regionDisabled"
            :consistent-menu-width="false"
          />
        </FormField>
        <FormField label="区间" hint="未开启多区间的租户锁定根 compartment">
          <NSelect
            v-model:value="form.snatchCompartmentId"
            :options="compOptions"
            :loading="compartments.loading.value"
            :disabled="compDisabled"
          />
        </FormField>
      </div>

      <FormSection title="实例规格（与创建实例一致）" />
      <FormField label="实例名" required>
        <NInput v-model:value="form.displayName" placeholder="my-a1" />
      </FormField>
      <InstanceSpecForm
        ref="specForm"
        :cfg-id="form.snatchCfgId"
        :region="form.snatchRegion || undefined"
        :compartment-id="form.snatchCompartmentId || undefined"
        ad-hint="留空 = 自动轮询区域全部可用域,每次执行换下一个(ad-1 → ad-2 → … 循环)"
      />
      <div class="mt-2 text-xs leading-relaxed text-ink-3">
        VCN 选「自动创建」时复用（或创建）oci-portal-auto-vcn 公网子网 ·
        每次调度按剩余台数尝试创建，容量不足自动等待下次执行
      </div>
    </template>
  </FormModal>
</template>
