import { ref, computed } from 'vue'
import { http } from '@/api'
import { useSnackbar } from '@/composables/useSnackbar'
import { registerServerBatchJob } from '@/composables/useScriptExecutionQueue'
import { showScriptSubmitToast } from '@/composables/useScriptSubmitToast'
import { formatApiError } from '@/utils/apiError'
import type { FilesElevationMode, ServerApiItem } from '@/types/api'

export type ServerAuthMethod = 'password' | 'key'

export interface ServerFormModel {
  name: string
  domain: string
  port: number
  username: string
  auth_method: ServerAuthMethod
  preset_id: number | null
  password: string
  ssh_key_preset_id: number | null
  ssh_key_path: string
  ssh_key_private: string
  target_path: string
  category: string
  platform_id: number | null
  node_id: number | null
  files_elevation: FilesElevationMode
}

export interface PresetOption {
  id: number
  name: string
  username?: string
  display?: string
}

function emptyForm(): ServerFormModel {
  return {
    name: '',
    domain: '',
    port: 22,
    username: 'root',
    auth_method: 'password',
    preset_id: null,
    password: '',
    ssh_key_preset_id: null,
    ssh_key_path: '',
    ssh_key_private: '',
    target_path: '/www/wwwroot',
    category: '',
    platform_id: null,
    node_id: null,
    files_elevation: 'auto_sudo',
  }
}

function categoryLabel(name: string): string {
  return name === 'uncategorized' ? '' : name
}

function fromServer(item: ServerApiItem): ServerFormModel {
  const auth = item.auth_method === 'key' ? 'key' : 'password'
  return {
    name: item.name,
    domain: item.domain || '',
    port: item.port ?? 22,
    username: item.username || 'root',
    auth_method: auth,
    preset_id: item.preset_id,
    password: '',
    ssh_key_preset_id: item.ssh_key_preset_id,
    ssh_key_path: item.ssh_key_path || '',
    ssh_key_private: '',
    target_path: item.target_path || '/www/wwwroot',
    category: item.category || '',
    platform_id: item.platform_id,
    node_id: item.node_id,
    files_elevation: item.files_elevation || 'auto_sudo',
  }
}

export function useServerFormDialog(onSaved: () => void) {
  const snackbar = useSnackbar()

  const visible = ref(false)
  const editingId = ref<number | null>(null)
  const saving = ref(false)
  const form = ref<ServerFormModel>(emptyForm())
  const editHints = ref({ password_set: false, ssh_key_private_set: false })

  const passwordPresets = ref<PresetOption[]>([])
  const sshKeyPresets = ref<PresetOption[]>([])
  const categoryOptions = ref<string[]>([])
  const platforms = ref<{ id: number; name: string }[]>([])
  const nodes = ref<{ id: number; name: string }[]>([])
  const lookupsLoading = ref(false)

  const isEditing = computed(() => editingId.value != null)

  const filesElevationItems = [
    { title: '自动（权限失败时 sudo）', value: 'auto_sudo' as const },
    { title: '关闭 sudo', value: 'off' as const },
    { title: '始终 sudo（排障）', value: 'always_sudo' as const },
  ]

  async function loadLookups() {
    lookupsLoading.value = true
    try {
      const [catRes, pwRes, skRes, nodesRes] = await Promise.all([
        http.get<{
          categories: { name: string; count: number }[]
          platforms: { id: number; name: string; category: string }[]
        }>('/servers/categories'),
        http.getList<PresetOption>('/presets/', { per_page: 500 }),
        http.getList<PresetOption>('/ssh-key-presets/', { per_page: 500 }),
        http.get<{ id: number; name: string }[]>('/assets/nodes'),
      ])
      const names = new Set<string>()
      for (const c of catRes.categories || []) {
        const label = categoryLabel(c.name)
        if (label) names.add(label)
      }
      categoryOptions.value = [...names].sort((a, b) => a.localeCompare(b, 'zh'))
      platforms.value = (catRes.platforms || []).map((p) => ({ id: p.id, name: p.name }))
      passwordPresets.value = (pwRes.items || []).map((p) => ({
        ...p,
        display: `${p.name} (${p.username || 'root'})`,
      }))
      sshKeyPresets.value = (skRes.items || []).map((p) => ({
        ...p,
        display: `${p.name} (${p.username || 'root'})`,
      }))
      nodes.value = Array.isArray(nodesRes) ? nodesRes : []
    } catch {
      snackbar('加载表单选项失败', 'error')
    } finally {
      lookupsLoading.value = false
    }
  }

  async function openAdd() {
    editingId.value = null
    form.value = emptyForm()
    editHints.value = { password_set: false, ssh_key_private_set: false }
    visible.value = true
    await loadLookups()
  }

  async function openEdit(item: ServerApiItem) {
    editingId.value = item.id
    form.value = fromServer(item)
    editHints.value = {
      password_set: Boolean(item.password_set),
      ssh_key_private_set: Boolean(item.ssh_key_private_set || item.ssh_key_configured),
    }
    visible.value = true
    await loadLookups()
  }

  function close() {
    visible.value = false
    editingId.value = null
    form.value = emptyForm()
    editHints.value = { password_set: false, ssh_key_private_set: false }
  }

  function validate(): string | null {
    const f = form.value
    if (!f.name.trim()) return '请填写服务器名称'
    if (!f.domain.trim()) return '请填写域名或 IP'
    if (!f.username.trim()) return '请填写 SSH 用户名'
    const port = Number(f.port)
    if (!Number.isFinite(port) || port < 1 || port > 65535) return 'SSH 端口无效'
    const tp = f.target_path.trim()
    if (!tp) return '请填写目标路径（部署根目录）'
    if (!tp.startsWith('/')) return '目标路径须为绝对路径（以 / 开头）'

    if (f.auth_method === 'password') {
      if (!isEditing.value && !f.preset_id && !f.password.trim()) {
        return '请选择密码预设或填写 SSH 密码（安装 Agent 需要可 SSH）'
      }
    } else {
      if (!isEditing.value && !f.ssh_key_preset_id && !f.ssh_key_private.trim()) {
        return '请选择 SSH 密钥预设或粘贴私钥'
      }
    }
    return null
  }

  function buildPayload(): Record<string, unknown> {
    const f = form.value
    const base: Record<string, unknown> = {
      name: f.name.trim(),
      domain: f.domain.trim(),
      port: Number(f.port),
      username: f.username.trim() || 'root',
      auth_method: f.auth_method,
      target_path: f.target_path.trim(),
      category: f.category.trim() || undefined,
      platform_id: f.platform_id ?? undefined,
      node_id: f.node_id ?? undefined,
      files_elevation: f.files_elevation,
      agent_port: 8601,
    }

    if (f.auth_method === 'password') {
      if (f.preset_id != null) base.preset_id = f.preset_id
      if (f.password.trim()) base.password = f.password.trim()
      if (!isEditing.value) {
        if (!f.preset_id && !f.password.trim()) {
          /* validated above */
        }
      }
    } else {
      if (f.ssh_key_preset_id != null) base.ssh_key_preset_id = f.ssh_key_preset_id
      if (f.ssh_key_path.trim()) base.ssh_key_path = f.ssh_key_path.trim()
      if (f.ssh_key_private.trim()) base.ssh_key_private = f.ssh_key_private.trim()
    }

    return base
  }

  async function save() {
    const err = validate()
    if (err) {
      snackbar(err, 'error')
      return
    }
    saving.value = true
    try {
      const payload = buildPayload()
      if (isEditing.value && editingId.value != null) {
        await http.put(`/servers/${editingId.value}`, payload)
      } else {
        const created = await http.post<ServerApiItem & {
          onboarding_job_id?: number
          onboarding_label?: string
        }>('/servers/', payload)
        if (created.onboarding_job_id) {
          registerServerBatchJob(
            created.onboarding_job_id,
            created.onboarding_label || '新服务器初始化',
            1,
            'onboard',
            '0/1',
            'running',
          )
          showScriptSubmitToast('新服务器初始化已在后台执行（sudo + 路径探测）')
        }
        if (created.agent_action === 'install') {
          showScriptSubmitToast(
            created.agent_action_message || '请展开服务器行并点击「安装 Agent」以采集 CPU/内存等指标',
          )
        }
      }
      close()
      onSaved()
      snackbar('保存成功')
    } catch (e: unknown) {
      snackbar(formatApiError(e, '保存失败'), 'error')
    } finally {
      saving.value = false
    }
  }

  return {
    visible,
    editingId,
    isEditing,
    saving,
    form,
    editHints,
    passwordPresets,
    sshKeyPresets,
    categoryOptions,
    platforms,
    nodes,
    lookupsLoading,
    filesElevationItems,
    openAdd,
    openEdit,
    close,
    save,
  }
}
