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

import { listShapes, updateInstance } from '@/api/instances'
import AppInputNumber from '@/components/AppInputNumber.vue'
import FormField from '@/components/FormField.vue'
import FormModal from '@/components/FormModal.vue'
import { useAsync } from '@/composables/useAsync'
import { useToast } from '@/composables/useToast'

const props = defineProps<{
  show: boolean
  cfgId: number
  instanceId: string
  region?: string
  shape: string
  ocpus: number
  memoryInGBs: number
}>()

const emit = defineEmits<{ 'update:show': [boolean]; resized: [] }>()

const message = useToast()
const submitting = ref(false)
const form = reactive({ shape: props.shape, ocpus: props.ocpus, memoryInGBs: props.memoryInGBs })

const shapes = useAsync(() => listShapes(props.cfgId, props.region).catch(() => []), false)

watch(
  () => props.show,
  (v) => {
    if (v) {
      Object.assign(form, { shape: props.shape, ocpus: props.ocpus, memoryInGBs: props.memoryInGBs })
      void shapes.run()
    }
  },
)

const BILLING_LABEL: Record<string, string> = {
  ALWAYS_FREE: '永久免费',
  LIMITED_FREE: '免费额度',
}

const shapeOptions = computed(() => {
  const list = shapes.data.value ?? []
  if (!list.length) return [{ label: form.shape, value: form.shape }]
  return list.map((s) => {
    const free = BILLING_LABEL[s.billingType]
    return { label: s.name + (free ? `（${free}）` : ''), value: s.name }
  })
})

const selectedShape = computed(
  () => shapes.data.value?.find((s) => s.name === form.shape) ?? null,
)
const isFlex = computed(() => selectedShape.value?.isFlexible ?? form.shape.endsWith('.Flex'))

const ocpuRange = computed(() => ({
  min: selectedShape.value?.ocpusMin || 1,
  max: selectedShape.value?.ocpusMax || 128,
}))
const memRange = computed(() => ({
  min: selectedShape.value?.memoryMinGBs || 1,
  max: selectedShape.value?.memoryMaxGBs || 1024,
}))

// 换 shape 后把规格收敛到该 shape 的合法范围
watch(selectedShape, (s) => {
  if (!s?.isFlexible) return
  form.ocpus = Math.min(Math.max(form.ocpus, ocpuRange.value.min), ocpuRange.value.max)
  form.memoryInGBs = Math.min(Math.max(form.memoryInGBs, memRange.value.min), memRange.value.max)
})

const changed = computed(
  () =>
    form.shape !== props.shape ||
    (isFlex.value && (form.ocpus !== props.ocpus || form.memoryInGBs !== props.memoryInGBs)),
)

async function submit() {
  submitting.value = true
  try {
    await updateInstance(
      props.cfgId,
      props.instanceId,
      {
        shape: form.shape !== props.shape ? form.shape : undefined,
        ocpus: isFlex.value ? form.ocpus : undefined,
        memoryInGBs: isFlex.value ? form.memoryInGBs : undefined,
      },
      props.region,
    )
    message.success('规格调整已提交，运行中实例将自动重启')
    emit('update:show', false)
    emit('resized')
  } catch (e) {
    message.error(e instanceof Error ? e.message : '调整失败')
  } finally {
    submitting.value = false
  }
}
</script>

<template>
  <FormModal
    :show="show"
    title="调整规格"
    :submitting="submitting"
    :submit-disabled="!changed"
    submit-text="提交调整"
    @update:show="emit('update:show', $event)"
    @submit="submit"
  >
    <FormField label="Shape" required hint="可更换为租户当前区域提供的其他 shape，需目标容量可用">
      <NSelect
        v-model:value="form.shape"
        filterable
        tag
        :options="shapeOptions"
        :loading="shapes.loading.value"
      />
    </FormField>
    <div v-if="isFlex" class="grid grid-cols-2 gap-3 max-md:grid-cols-1">
      <FormField label="OCPU" required :hint="`可选 ${ocpuRange.min}–${ocpuRange.max}`">
        <AppInputNumber
          v-model:value="form.ocpus"
          :min="ocpuRange.min"
          :max="ocpuRange.max"
          class="w-full"
        />
      </FormField>
      <FormField label="内存（GB）" required :hint="`可选 ${memRange.min}–${memRange.max} GB`">
        <AppInputNumber
          v-model:value="form.memoryInGBs"
          :min="memRange.min"
          :max="memRange.max"
          class="w-full"
        />
      </FormField>
    </div>
    <div class="text-xs leading-relaxed text-ink-3">
      运行中实例 OCI 会自动重启完成变更；更换 shape 要求镜像与目标 shape 架构兼容（如 aarch64
      镜像不能换到 x86 shape）。A1 免费额度上限 4 OCPU / 24 GB。
    </div>
  </FormModal>
</template>
