<script setup lang="ts">
import { computed, onMounted, ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
import { storeToRefs } from 'pinia'
import {
  createCompetition,
  deleteTrack,
  getCompetition,
  listTracks,
  updateCompetition,
  patchCompetition,
  createTrack,
  updateTrack,
} from '../../../api/admin/competitions'
import * as formSchemaApi from '../../../api/admin/formSchemas'
import type {
  CompetitionPayload,
  CompetitionRow,
  CompetitionTrackPayload,
  CompetitionTrackRow,
  FormSchemaRow,
} from '../../../api/admin/types'
import {
  brandingFormFromApi,
  brandingJsonFromForm,
  emptyBrandingForm,
  type BrandingForm,
} from '../../../utils/competitionBranding'
import { adminUseMock } from '../../../config/api'
import { useAdminCompetitionStore } from '../../../stores/adminCompetition'
import FormSchemaVisualEditor from '../../../components/admin/FormSchemaVisualEditor.vue'
import PledgeRichTextEditor from '../../../components/admin/PledgeRichTextEditor.vue'
import {
  editorItemsToSchemaJson,
  schemaJsonToEditorItems,
  validateEditorItems,
  type FormSchemaEditorItem,
  type FormSchemaPurpose,
} from '../../../utils/formSchemaEditor'

type TabKey = 'basic' | 'tracks' | 'signupForm' | 'review' | 'brand'

const TAB_ITEMS: { tab: TabKey; label: string }[] = [
  { tab: 'basic', label: '基础信息' },
  { tab: 'tracks', label: '赛道管理' },
  { tab: 'signupForm', label: '报名表配置' },
  { tab: 'review', label: '评审与计分' },
  { tab: 'brand', label: '品牌与文案' },
]

const router = useRouter()
const route = useRoute()
const competitionStore = useAdminCompetitionStore()
const { selectedCompetitionId } = storeToRefs(competitionStore)

const isCreate = () => route.name === 'admin-competition-new'

const competitionId = ref<number | null>(null)
const noContext = ref(false)
const detailRow = ref<CompetitionRow | null>(null)

const form = ref({
  slug: '',
  name: '',
  description: '',
  status: 'draft',
  published: false,
  signup_open_at: null as string | null,
  signup_close_at: null as string | null,
  pledge_content_html: '',
})

const brand = ref<BrandingForm>(emptyBrandingForm())
const tracks = ref<CompetitionTrackRow[]>([])
const tracksLoading = ref(false)

const trackDialogVisible = ref(false)
const trackEditingId = ref<number | null>(null)
const trackForm = ref({
  track_code: '',
  title: '',
  description: '',
  sort: 0,
  is_enabled: true,
})

const saving = ref(false)
const tabKey = ref<TabKey>('basic')

const signupSchemas = ref<FormSchemaRow[]>([])
const reviewSchemas = ref<FormSchemaRow[]>([])
const schemaBusy = ref(false)

const scoringJsonText = ref('')
const scoringError = ref('')

type ScoringDimRow = { key: string; weight: number; min: number; max: number }

function emptyScoringDimRow(): ScoringDimRow {
  return { key: '', weight: 1, min: 0, max: 100 }
}

const scoringDimRows = ref<ScoringDimRow[]>([emptyScoringDimRow()])

const schemaDialogVisible = ref(false)
const schemaModalPurpose = ref<'signup' | 'review'>('signup')
const schemaNewName = ref('')

const editSchemaDialogVisible = ref(false)
const editSchemaId = ref<number | null>(null)
const editSchemaName = ref('')
const editSchemaJsonText = ref('[]')
const editSchemaError = ref('')
const editSchemaPurpose = ref<FormSchemaPurpose>('signup')
const editSchemaVisualItems = ref<FormSchemaEditorItem[]>([])
const editSchemaTab = ref<'visual' | 'json'>('visual')

const headline = computed(() => {
  if (isCreate()) return '新建赛事'
  const sub = TAB_ITEMS.find((x) => x.tab === tabKey.value)?.label
  return sub ? `赛事中心 · ${sub}` : '赛事中心'
})

/** 切换赛事或新建时重建富文本编辑器，避免内容与实例不同步 */
const pledgeEditorKey = computed(() => String(competitionId.value ?? 'new'))

/** 当前站点下的绝对 URL（与 `createWebHistory(import.meta.env.BASE_URL)` 一致） */
function absoluteUrlFromPath(path: string): string {
  const origin = typeof window !== 'undefined' ? window.location.origin : ''
  const basePath = (import.meta.env.BASE_URL || '/').replace(/\/$/, '')
  const normalized = path.startsWith('/') ? path : `/${path}`
  return `${origin}${basePath}${normalized}`
}

/** 访问地址 slug 非空时：选手登录、评审端；并始终附管理端入口（与文档 `/c/{slug}/…`、`/admin/…` 一致） */
const portalLinkRows = computed(() => {
  const slug = form.value.slug.trim()
  const rows: { label: string; href: string }[] = []
  if (slug) {
    const enc = encodeURIComponent(slug)
    rows.push({
      label: '选手登录',
      href: absoluteUrlFromPath(`/c/${enc}/login`),
    })
    rows.push({
      label: '评审端入口',
      href: absoluteUrlFromPath(`/c/${enc}/review`),
    })
  }
  rows.push({
    label: '管理后台登录',
    href: absoluteUrlFromPath('/admin/login'),
  })
  rows.push({
    label: '赛事工作台（需先登录管理端）',
    href: absoluteUrlFromPath('/admin/competition/workspace'),
  })
  return rows
})

watch(editSchemaTab, (t) => {
  if (t === 'json') {
    syncEditVisualToJson()
  } else {
    syncEditJsonToVisual()
  }
})

function syncEditVisualToJson() {
  editSchemaJsonText.value = JSON.stringify(
    editorItemsToSchemaJson(editSchemaVisualItems.value, editSchemaPurpose.value),
    null,
    2,
  )
}

function syncEditJsonToVisual() {
  try {
    const raw = JSON.parse(editSchemaJsonText.value)
    if (Array.isArray(raw)) {
      editSchemaVisualItems.value = schemaJsonToEditorItems(raw, editSchemaPurpose.value)
    }
  } catch {
    /* 保留当前可视化内容 */
  }
}

watch(
  () => route.name,
  (n) => {
    if (n === 'admin-competition-new' || n === 'admin-competition-edit') {
      tabKey.value = 'basic'
    }
  },
  { immediate: true },
)

watch(tabKey, (t) => {
  const cid = competitionId.value
  if (!cid) return
  if (t === 'signupForm') void refreshSignupSchemas()
  if (t === 'review') {
    void refreshReviewSchemas()
  }
})

function resolveCompetitionId(): number | null {
  if (isCreate()) return null
  const raw = route.params.id
  if (raw != null && String(raw) !== '') {
    const n = Number(raw)
    if (Number.isFinite(n)) return n
  }
  const sid = selectedCompetitionId.value
  return sid && sid > 0 ? sid : null
}

/** API ISO → Element Plus 日期时间选择器（value-format `YYYY-MM-DD HH:mm:ss`，本地时区） */
function toElDateTimeModel(iso: string | null | undefined): string | null {
  if (!iso) return null
  const d = new Date(iso)
  if (Number.isNaN(d.getTime())) return null
  const pad = (n: number) => String(n).padStart(2, '0')
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`
}

function fromDatetimePickerValue(s: string | null | undefined): string | null {
  const t = (s ?? '').trim()
  if (!t) return null
  const forParse = t.includes('T') ? t : t.replace(/^(\d{4}-\d{2}-\d{2})\s+/, '$1T')
  const d = new Date(forParse)
  if (Number.isNaN(d.getTime())) return null
  return d.toISOString()
}

async function refreshSignupSchemas() {
  const cid = competitionId.value
  if (!cid) return
  schemaBusy.value = true
  try {
    signupSchemas.value = await formSchemaApi.listFormSchemas(cid, 'signup')
  } catch (e) {
    ElMessage.error(e instanceof Error ? e.message : '加载报名表列表失败')
  } finally {
    schemaBusy.value = false
  }
}

async function refreshReviewSchemas() {
  const cid = competitionId.value
  if (!cid) return
  schemaBusy.value = true
  try {
    reviewSchemas.value = await formSchemaApi.listFormSchemas(cid, 'review')
  } catch (e) {
    ElMessage.error(e instanceof Error ? e.message : '加载评审表列表失败')
  } finally {
    schemaBusy.value = false
  }
}

function loadScoringFromRow(row: CompetitionRow) {
  scoringJsonText.value =
    row.scoring_rules_json != null ? JSON.stringify(row.scoring_rules_json, null, 2) : ''
  syncScoringDimRowsFromJson()
}

function syncScoringDimRowsFromJson() {
  const t = scoringJsonText.value.trim()
  if (!t) {
    scoringDimRows.value = [emptyScoringDimRow()]
    return
  }
  try {
    const o = JSON.parse(t) as {
      dimensions?: Array<{ key?: string; weight?: number; min?: number; max?: number }>
    }
    if (o && Array.isArray(o.dimensions) && o.dimensions.length > 0) {
      scoringDimRows.value = o.dimensions.map((d) => ({
        key: String(d.key ?? ''),
        weight: Number(d.weight) || 0,
        min: d.min != null ? Number(d.min) : 0,
        max: d.max != null ? Number(d.max) : 100,
      }))
      return
    }
  } catch {
    /* 结构不符合快捷表时保持默认一行 */
  }
  scoringDimRows.value = [emptyScoringDimRow()]
}

function addScoringDimRow() {
  scoringDimRows.value = [...scoringDimRows.value, emptyScoringDimRow()]
}

function removeScoringDimRow(i: number) {
  if (scoringDimRows.value.length <= 1) {
    scoringDimRows.value = [emptyScoringDimRow()]
    return
  }
  scoringDimRows.value = scoringDimRows.value.filter((_, idx) => idx !== i)
}

function buildScoringRulesFromSimpleDims(dims: ScoringDimRow[]): Record<string, unknown> {
  const dimensions = dims
    .filter((r) => r.key.trim())
    .map((r) => ({
      key: r.key.trim(),
      weight: Number(r.weight) || 0,
      min: Number(r.min) || 0,
      max: Number(r.max) || 0,
    }))
  return {
    version: 1,
    source: 'custom',
    dimensions,
    line_total_formula: 'weighted_sum',
    team_aggregate: {
      sum: { on: 'line_total' },
      average: { on: 'line_total', denominator: 'submitted_review_count' },
    },
    constraints: { min_reviewers_for_avg: 1 },
    tie_break: 'submitted_at_asc',
  }
}

function applySimpleScoringToJson() {
  const withKey = scoringDimRows.value.filter((r) => r.key.trim())
  if (!withKey.length) {
    ElMessage.warning('请至少填写一行「维度 key」（须与评审表 number 类字段的 name/key 一致）')
    return
  }
  const obj = buildScoringRulesFromSimpleDims(scoringDimRows.value)
  scoringJsonText.value = JSON.stringify(obj, null, 2)
  scoringError.value = ''
}

async function loadDetail() {
  if (isCreate()) {
    competitionId.value = null
    noContext.value = false
    detailRow.value = null
    brand.value = emptyBrandingForm()
    form.value = {
      slug: '',
      name: '',
      description: '',
      status: 'draft',
      published: false,
      signup_open_at: null,
      signup_close_at: null,
      pledge_content_html: '',
    }
    return
  }
  const id = resolveCompetitionId()
  if (!id) {
    competitionId.value = null
    detailRow.value = null
    noContext.value = true
    return
  }
  noContext.value = false
  competitionId.value = id
  competitionStore.selectCompetition(id)

  try {
    const row = await getCompetition(id)
    detailRow.value = row
    form.value = {
      slug: row.slug,
      name: row.name,
      description: row.description ?? '',
      status: row.status,
      published: row.published,
      signup_open_at: toElDateTimeModel(row.signup_open_at),
      signup_close_at: toElDateTimeModel(row.signup_close_at),
      pledge_content_html: row.pledge_content_html ?? '',
    }
    brand.value = brandingFormFromApi(row.branding_json)
    loadScoringFromRow(row)
    await refreshTracks()
    if (tabKey.value === 'signupForm') await refreshSignupSchemas()
    if (tabKey.value === 'review') await refreshReviewSchemas()
  } catch (e) {
    detailRow.value = null
    ElMessage.error(e instanceof Error ? e.message : '加载赛事详情失败')
  }
}

watch(
  () => [route.fullPath, selectedCompetitionId.value] as const,
  () => {
    void loadDetail()
  },
)

function buildPayload(): CompetitionPayload {
  const branding_json = brandingJsonFromForm(brand.value) as CompetitionPayload['branding_json']
  return {
    slug: form.value.slug.trim(),
    name: form.value.name.trim(),
    description: form.value.description.trim() || null,
    status: form.value.status,
    published: form.value.published,
    signup_open_at: fromDatetimePickerValue(form.value.signup_open_at),
    signup_close_at: fromDatetimePickerValue(form.value.signup_close_at),
    branding_json: branding_json ?? null,
    pledge_content_html: form.value.pledge_content_html?.trim() ? form.value.pledge_content_html : null,
  }
}

async function saveBasic() {
  saving.value = true
  try {
    const payload = buildPayload()
    if (!payload.slug || !payload.name) {
      ElMessage.warning('请填写赛事名称和访问地址')
      return
    }

    const creatingNew = route.name === 'admin-competition-new'
    if (creatingNew) {
      const row = await createCompetition(payload)
      competitionId.value = row.id
      competitionStore.selectCompetition(row.id)
      await router.replace({ name: 'admin-competition-workspace' })
      await loadDetail()
      if (adminUseMock()) {
        ElMessage.success(
          '已创建（当前为 Mock 模式，浏览器不会发 HTTP；联调请设 VITE_ADMIN_USE_MOCK=false 并重启 dev）',
        )
      } else {
        ElMessage.success('已创建，请在本页顶部标签切换「赛道 / 报名表」等模块继续配置')
      }
      return
    }

    if (competitionId.value) {
      await updateCompetition(competitionId.value, payload)
      ElMessage.success('已保存')
      await loadDetail()
    } else {
      ElMessage.warning('无法保存：请先选择一场赛事，或从「新建赛事」进入后再试')
    }
  } catch (e) {
    ElMessage.error(e instanceof Error ? e.message : '保存失败')
  } finally {
    saving.value = false
  }
}

function openTrackModal(row?: CompetitionTrackRow) {
  if (!competitionId.value) {
    ElMessage.warning('请先保存赛事基础信息')
    return
  }
  trackEditingId.value = row?.id ?? null
  trackForm.value = row
    ? {
        track_code: row.track_code,
        title: row.title,
        description: row.description ?? '',
        sort: row.sort,
        is_enabled: row.is_enabled,
      }
    : {
        track_code: '',
        title: '',
        description: '',
        sort: (tracks.value[tracks.value.length - 1]?.sort ?? 0) + 10,
        is_enabled: true,
      }
  trackDialogVisible.value = true
}

async function refreshTracks() {
  const cid = competitionId.value
  if (!cid) {
    tracks.value = []
    return
  }
  tracksLoading.value = true
  try {
    tracks.value = await listTracks(cid)
  } finally {
    tracksLoading.value = false
  }
}

async function saveTrack() {
  const cid = competitionId.value
  if (!cid) return
  const payload: CompetitionTrackPayload = {
    track_code: trackForm.value.track_code.trim(),
    title: trackForm.value.title.trim(),
    description: trackForm.value.description.trim() || null,
    sort: Number(trackForm.value.sort) || 0,
    is_enabled: trackForm.value.is_enabled,
  }
  if (!payload.track_code || !payload.title) {
    ElMessage.warning('请填写赛道编码与标题')
    return
  }
  try {
    if (trackEditingId.value) {
      await updateTrack(cid, trackEditingId.value, payload)
    } else {
      await createTrack(cid, payload)
    }
    trackDialogVisible.value = false
    await refreshTracks()
    ElMessage.success('赛道已保存')
  } catch (e) {
    ElMessage.error(e instanceof Error ? e.message : '保存赛道失败')
  }
}

async function removeTrack(row: CompetitionTrackRow) {
  const cid = competitionId.value
  if (!cid) return
  try {
    await ElMessageBox.confirm(`确定删除赛道「${row.title}」？`, '确认删除', {
      confirmButtonText: '删除',
      cancelButtonText: '取消',
      type: 'warning',
    })
  } catch {
    return
  }
  try {
    await deleteTrack(cid, row.id)
    await refreshTracks()
    ElMessage.success('已删除')
  } catch (e) {
    ElMessage.error(e instanceof Error ? e.message : '删除失败')
  }
}

function previewSchemaLabels(fieldLabels: string[] | undefined, json: unknown): string {
  if (fieldLabels?.length) return fieldLabels.join('、')
  if (!Array.isArray(json)) return '—'
  const parts = json
    .map((item) =>
      item !== null && typeof item === 'object' && 'label' in item
        ? String((item as { label?: string }).label || '')
        : '',
    )
    .filter(Boolean)
  return parts.length ? parts.join('、') : '—'
}

function openNewSchemaModal(purpose: 'signup' | 'review') {
  if (!competitionId.value) {
    ElMessage.warning('请先选择或创建赛事')
    return
  }
  schemaModalPurpose.value = purpose
  schemaNewName.value = purpose === 'signup' ? '报名表新版本' : '评审表新版本'
  schemaDialogVisible.value = true
}

async function confirmNewSchema() {
  const cid = competitionId.value
  if (!cid) return
  const name = schemaNewName.value.trim()
  if (!name) {
    ElMessage.warning('请填写版本名称')
    return
  }
  try {
    await formSchemaApi.createFormSchema(cid, {
      purpose: schemaModalPurpose.value,
      name,
      schema_json: [],
      is_published: false,
    })
    schemaDialogVisible.value = false
    if (schemaModalPurpose.value === 'signup') await refreshSignupSchemas()
    else await refreshReviewSchemas()
    ElMessage.success('已创建表单版本')
  } catch (e) {
    ElMessage.error(e instanceof Error ? e.message : '创建失败')
  }
}

async function bindSignup(id: number) {
  const cid = competitionId.value
  if (!cid) return
  try {
    await patchCompetition(cid, { form_schema_id: id })
    ElMessage.success('已设为当前报名表')
    await loadDetail()
    await refreshSignupSchemas()
  } catch (e) {
    ElMessage.error(e instanceof Error ? e.message : '绑定失败')
  }
}

async function bindReview(id: number) {
  const cid = competitionId.value
  if (!cid) return
  try {
    await patchCompetition(cid, { review_form_schema_id: id })
    ElMessage.success('已设为当前评审表')
    await loadDetail()
    await refreshReviewSchemas()
  } catch (e) {
    ElMessage.error(e instanceof Error ? e.message : '绑定失败')
  }
}

async function togglePublish(row: FormSchemaRow, next: boolean) {
  const cid = competitionId.value
  if (!cid) return
  try {
    await formSchemaApi.updateFormSchema(cid, row.id, { is_published: next })
    if (row.purpose === 'signup') await refreshSignupSchemas()
    else await refreshReviewSchemas()
  } catch (e) {
    ElMessage.error(e instanceof Error ? e.message : '更新失败')
  }
}

async function openEditSchema(row: FormSchemaRow) {
  const cid = competitionId.value
  if (!cid) return
  editSchemaError.value = ''
  try {
    const full = await formSchemaApi.getFormSchema(cid, row.id)
    editSchemaId.value = full.id
    editSchemaName.value = full.name
    editSchemaPurpose.value = full.purpose
    editSchemaVisualItems.value = schemaJsonToEditorItems(full.schema_json ?? [], full.purpose)
    editSchemaJsonText.value = JSON.stringify(full.schema_json ?? [], null, 2)
    editSchemaTab.value = 'visual'
  } catch (e) {
    ElMessage.error(e instanceof Error ? e.message : '加载表单详情失败')
    return
  }
  editSchemaDialogVisible.value = true
}

async function saveEditedSchema() {
  const cid = competitionId.value
  const sid = editSchemaId.value
  if (!cid || !sid) return
  editSchemaError.value = ''
  let parsed: unknown[] = []
  if (editSchemaTab.value === 'visual') {
    const err = validateEditorItems(editSchemaVisualItems.value, editSchemaPurpose.value)
    if (err) {
      editSchemaError.value = err
      return
    }
    parsed = editorItemsToSchemaJson(editSchemaVisualItems.value, editSchemaPurpose.value) as unknown[]
  } else {
    try {
      const raw = JSON.parse(editSchemaJsonText.value)
      if (!Array.isArray(raw)) {
        editSchemaError.value = '表单定义须为 JSON 数组'
        return
      }
      parsed = raw as unknown[]
    } catch {
      editSchemaError.value = 'JSON 格式不正确'
      return
    }
  }
  try {
    await formSchemaApi.updateFormSchema(cid, sid, {
      name: editSchemaName.value.trim() || undefined,
      schema_json: parsed,
    })
    editSchemaDialogVisible.value = false
    await refreshSignupSchemas()
    await refreshReviewSchemas()
    ElMessage.success('表单已保存')
  } catch (e) {
    ElMessage.error(e instanceof Error ? e.message : '保存失败')
  }
}

async function removeSchema(row: FormSchemaRow) {
  const cid = competitionId.value
  if (!cid) return
  try {
    await ElMessageBox.confirm(`确定删除表单版本「${row.name}」？`, '确认删除', {
      confirmButtonText: '删除',
      cancelButtonText: '取消',
      type: 'warning',
    })
  } catch {
    return
  }
  try {
    await formSchemaApi.deleteFormSchema(cid, row.id)
    if (row.purpose === 'signup') await refreshSignupSchemas()
    else await refreshReviewSchemas()
    await loadDetail()
    ElMessage.success('已删除')
  } catch (e) {
    ElMessage.error(e instanceof Error ? e.message : '删除失败')
  }
}

async function saveScoringRules() {
  const cid = competitionId.value
  if (!cid) return
  scoringError.value = ''
  let parsed: unknown = null
  const t = scoringJsonText.value.trim()
  if (t) {
    try {
      parsed = JSON.parse(t) as unknown
      if (parsed !== null && typeof parsed !== 'object') {
        scoringError.value = '计分规则须为 JSON 对象'
        return
      }
    } catch {
      scoringError.value = 'JSON 格式不正确'
      return
    }
  }
  try {
    await patchCompetition(cid, { scoring_rules_json: parsed })
    ElMessage.success('计分规则已保存（留空表示使用系统默认）')
    await loadDetail()
  } catch (e) {
    ElMessage.error(e instanceof Error ? e.message : '保存失败')
  }
}

async function clearReviewBinding() {
  const cid = competitionId.value
  if (!cid) return
  try {
    await patchCompetition(cid, { review_form_schema_id: null })
    ElMessage.success('已取消评审表绑定，将使用系统默认评审项')
    await loadDetail()
    await refreshReviewSchemas()
  } catch (e) {
    ElMessage.error(e instanceof Error ? e.message : '操作失败')
  }
}

onMounted(() => {
  void loadDetail()
})
</script>

<template>
  <div class="competition-workspace">
    <div class="workspace-header">
      <h2 class="workspace-title">{{ headline }}</h2>
      <el-button class="workspace-back" @click="router.push({ name: 'admin-competitions-list' })">
        返回赛事列表
      </el-button>
    </div>

    <el-alert
      v-if="noContext && !isCreate()"
      title="请选择赛事"
      type="warning"
      :closable="false"
      show-icon
      class="workspace-alert"
    >
      请先在顶部<strong>赛事切换</strong>中选择一场赛事，或从「赛事列表」点击「进入赛事中心」。
    </el-alert>

    <el-tabs
      v-else
      v-model="tabKey"
      type="border-card"
      class="workspace-tabs"
      :class="{ 'workspace-tabs--create': isCreate() }"
    >
      <el-tab-pane label="基础信息" name="basic">
        <el-form label-position="top" class="workspace-form">
          <el-row :gutter="20">
            <el-col :xs="24" :md="12">
              <el-form-item label="赛事名称">
                <el-input v-model="form.name" autocomplete="off" />
              </el-form-item>
            </el-col>
            <el-col :xs="24" :md="12">
              <el-form-item label="访问地址">
                <el-input v-model="form.slug" autocomplete="off" />
                <p class="form-hint">用于选手端网址识别，可使用中文、数字、横线等，请勿含空格。</p>
                <div class="login-links-stack">
                  <p v-for="(row, idx) in portalLinkRows" :key="idx" class="form-hint login-link-preview">
                    <span class="login-link-label">{{ row.label }}</span>
                    <a :href="row.href" target="_blank" rel="noopener noreferrer" class="login-link-url">{{
                      row.href
                    }}</a>
                  </p>
                </div>
              </el-form-item>
            </el-col>
            <el-col :span="24">
              <el-form-item label="描述">
                <el-input v-model="form.description" type="textarea" :rows="3" />
              </el-form-item>
            </el-col>
            <el-col :span="24">
              <el-form-item label="参赛承诺书">
                <p class="form-hint pledge-hint">
                  以下为选手端签署弹窗中的正文（支持富文本）。若留空，将使用系统内置默认条款。
                </p>
                <PledgeRichTextEditor v-model="form.pledge_content_html" :key="pledgeEditorKey" />
              </el-form-item>
            </el-col>
            <el-col :xs="24" :sm="12" :md="8">
              <el-form-item label="状态">
                <el-select v-model="form.status" class="w-100">
                  <el-option label="草稿" value="draft" />
                  <el-option label="已发布" value="published" />
                  <el-option label="报名进行中" value="signup_open" />
                  <el-option label="报名已截止" value="signup_closed" />
                  <el-option label="评审中" value="reviewing" />
                  <el-option label="已结束" value="ended" />
                </el-select>
              </el-form-item>
            </el-col>
            <el-col :xs="24" :sm="12" :md="8">
              <el-form-item label="对外展示">
                <el-checkbox v-model="form.published">在选手端展示</el-checkbox>
              </el-form-item>
            </el-col>
            <el-col :xs="24" :md="12">
              <el-form-item label="报名开始">
                <el-date-picker
                  v-model="form.signup_open_at"
                  type="datetime"
                  placeholder="选择报名开始时间"
                  format="YYYY-MM-DD HH:mm"
                  value-format="YYYY-MM-DD HH:mm:ss"
                  class="w-100"
                  clearable
                />
              </el-form-item>
            </el-col>
            <el-col :xs="24" :md="12">
              <el-form-item label="报名截止">
                <el-date-picker
                  v-model="form.signup_close_at"
                  type="datetime"
                  placeholder="选择报名截止时间"
                  format="YYYY-MM-DD HH:mm"
                  value-format="YYYY-MM-DD HH:mm:ss"
                  class="w-100"
                  clearable
                />
              </el-form-item>
            </el-col>
          </el-row>
          <el-form-item>
            <el-button native-type="button" type="primary" :loading="saving" @click="saveBasic">
              {{ saving ? '保存中…' : '保存基础信息' }}
            </el-button>
          </el-form-item>
        </el-form>
      </el-tab-pane>

      <el-tab-pane v-if="!isCreate()" label="赛道管理" name="tracks" lazy>
        <div v-loading="tracksLoading" class="tracks-pane">
        <div class="pane-toolbar pane-toolbar--split">
          <p class="form-hint pane-toolbar-tip">维护本场主题赛道。新建赛事请先保存基础信息。</p>
          <el-button type="primary" @click="openTrackModal()">新增赛道</el-button>
        </div>
        <el-table :data="tracks" stripe border empty-text="暂无赛道" class="workspace-table">
          <el-table-column prop="track_code" label="赛道编码" min-width="120" />
          <el-table-column prop="title" label="标题" min-width="140" />
          <el-table-column prop="description" label="简介" min-width="180" show-overflow-tooltip>
            <template #default="{ row }">
              <span class="cell-muted">{{ row.description || '—' }}</span>
            </template>
          </el-table-column>
          <el-table-column prop="sort" label="排序" width="80" />
          <el-table-column label="启用" width="80">
            <template #default="{ row }">{{ row.is_enabled ? '是' : '否' }}</template>
          </el-table-column>
          <el-table-column label="操作" width="140" fixed="right" align="right">
            <template #default="{ row }">
              <el-button link type="primary" size="small" @click="openTrackModal(row)">编辑</el-button>
              <el-button link type="danger" size="small" @click="removeTrack(row)">删除</el-button>
            </template>
          </el-table-column>
        </el-table>
        </div>
      </el-tab-pane>

      <el-tab-pane v-if="!isCreate()" label="报名表配置" name="signupForm" lazy>
        <p class="form-hint">
          管理选手报名表字段定义（JSON 数组）。可创建多个版本，设为当前报名表后选手端生效。当前绑定编号：<strong>{{
            detailRow?.form_schema_id ?? '未绑定'
          }}</strong>
        </p>
        <div class="pane-toolbar">
          <el-button type="primary" :disabled="!competitionId" @click="openNewSchemaModal('signup')">
            新建报名表版本
          </el-button>
          <el-button :loading="schemaBusy" @click="refreshSignupSchemas">刷新列表</el-button>
        </div>
        <el-table :data="signupSchemas" stripe border empty-text="暂无版本，请先新建" class="workspace-table">
          <el-table-column prop="name" label="名称" min-width="120" />
          <el-table-column prop="version" label="版本" width="80" />
          <el-table-column label="发布" width="72">
            <template #default="{ row }">{{ row.is_published ? '是' : '否' }}</template>
          </el-table-column>
          <el-table-column label="当前" width="88">
            <template #default="{ row }">{{ row.is_current_signup ? '当前' : '—' }}</template>
          </el-table-column>
          <el-table-column label="字段预览" min-width="200">
            <template #default="{ row }">
              <span class="cell-muted">{{ previewSchemaLabels(row.field_labels, row.schema_json) }}</span>
            </template>
          </el-table-column>
          <el-table-column label="操作" width="260" fixed="right" align="right">
            <template #default="{ row }">
              <el-button link type="primary" size="small" @click="bindSignup(row.id)">设为当前</el-button>
              <el-button link type="primary" size="small" @click="openEditSchema(row)">编辑</el-button>
              <el-button link size="small" @click="togglePublish(row, !row.is_published)">
                {{ row.is_published ? '取消发布' : '发布' }}
              </el-button>
              <el-button link type="danger" size="small" @click="removeSchema(row)">删除</el-button>
            </template>
          </el-table-column>
        </el-table>
      </el-tab-pane>

      <el-tab-pane v-if="!isCreate()" label="评审与计分" name="review" lazy>
        <div class="section-block">
          <h3 class="section-title">评审打分表</h3>
          <p class="form-hint">
            配置评审端打分项（与报名表独立）。当前绑定编号：<strong>{{
              detailRow?.review_form_schema_id ?? '未绑定（系统默认）'
            }}</strong>
          </p>
          <div class="pane-toolbar">
            <el-button type="primary" :disabled="!competitionId" @click="openNewSchemaModal('review')">
              新建评审表版本
            </el-button>
            <el-button @click="clearReviewBinding">取消绑定（用默认）</el-button>
            <el-button :loading="schemaBusy" @click="refreshReviewSchemas">刷新列表</el-button>
          </div>
          <el-table :data="reviewSchemas" stripe border empty-text="暂无版本" class="workspace-table workspace-table--mb">
            <el-table-column prop="name" label="名称" min-width="120" />
            <el-table-column prop="version" label="版本" width="80" />
            <el-table-column label="发布" width="72">
              <template #default="{ row }">{{ row.is_published ? '是' : '否' }}</template>
            </el-table-column>
            <el-table-column label="当前" width="88">
              <template #default="{ row }">{{ row.is_current_review ? '当前' : '—' }}</template>
            </el-table-column>
            <el-table-column label="字段预览" min-width="200">
              <template #default="{ row }">
                <span class="cell-muted">{{ previewSchemaLabels(row.field_labels, row.schema_json) }}</span>
              </template>
            </el-table-column>
            <el-table-column label="操作" width="260" fixed="right" align="right">
              <template #default="{ row }">
                <el-button link type="primary" size="small" @click="bindReview(row.id)">设为当前</el-button>
                <el-button link type="primary" size="small" @click="openEditSchema(row)">编辑</el-button>
                <el-button link size="small" @click="togglePublish(row, !row.is_published)">
                  {{ row.is_published ? '取消发布' : '发布' }}
                </el-button>
                <el-button link type="danger" size="small" @click="removeSchema(row)">删除</el-button>
              </template>
            </el-table-column>
          </el-table>
        </div>

        <div class="section-block">
          <h3 class="section-title">计分规则</h3>
          <p class="form-hint">
            数据库中按 JSON 对象存储。留空并保存表示使用系统默认规则。多数情况只需填写各评分维度的 key 与权重，再点「生成 JSON」。
          </p>
          <p class="subsection-label">快捷配置（维度加权求和 → line_total）</p>
          <p class="form-hint">「维度 key」须与评审 Schema 里数字打分项的字段名一致。</p>
          <el-table :data="scoringDimRows" border size="small" class="workspace-table workspace-table--mb">
            <el-table-column label="维度 key" min-width="160">
              <template #default="{ row }">
                <el-input v-model="row.key" size="small" placeholder="如 dim_innovation" />
              </template>
            </el-table-column>
            <el-table-column label="权重" width="120">
              <template #default="{ row }">
                <el-input-number v-model="row.weight" size="small" :controls="false" class="w-100" />
              </template>
            </el-table-column>
            <el-table-column label="最低分" width="110">
              <template #default="{ row }">
                <el-input-number v-model="row.min" size="small" :controls="false" class="w-100" />
              </template>
            </el-table-column>
            <el-table-column label="最高分" width="110">
              <template #default="{ row }">
                <el-input-number v-model="row.max" size="small" :controls="false" class="w-100" />
              </template>
            </el-table-column>
            <el-table-column label="操作" width="88" align="right">
              <template #default="{ $index }">
                <el-button link type="danger" size="small" @click="removeScoringDimRow($index)">删行</el-button>
              </template>
            </el-table-column>
          </el-table>
          <div class="pane-toolbar pane-toolbar--wrap">
            <el-button @click="addScoringDimRow">增加一行</el-button>
            <el-button @click="syncScoringDimRowsFromJson">从下方 JSON 解析到表格</el-button>
            <el-button type="primary" @click="applySimpleScoringToJson">根据表格生成 JSON</el-button>
          </div>
          <p class="subsection-label">高级：直接编辑完整规则 JSON</p>
          <el-input v-model="scoringJsonText" type="textarea" :rows="8" class="json-area" spellcheck="false" />
          <el-alert v-if="scoringError" type="error" :closable="false" class="mt-2" :description="scoringError" />
          <el-button type="primary" class="mt-2" @click="saveScoringRules">保存计分规则</el-button>
        </div>
      </el-tab-pane>

      <el-tab-pane v-if="!isCreate()" label="品牌与文案" name="brand" lazy>
        <p class="form-hint">按页面分类填写对外展示文案。留空的项不会保存。</p>

        <h3 class="section-title">整站</h3>
        <el-form label-position="top" class="workspace-form">
          <el-form-item label="浏览器标签标题">
            <el-input v-model="brand.documentTitle" />
          </el-form-item>
        </el-form>

        <h3 class="section-title">登录页</h3>
        <el-form label-position="top" class="workspace-form">
          <el-row :gutter="16">
            <el-col :xs="24" :md="12">
              <el-form-item label="品牌辅助行"><el-input v-model="brand.login.markLine" /></el-form-item>
            </el-col>
            <el-col :xs="24" :md="12">
              <el-form-item label="主标题"><el-input v-model="brand.login.headline" /></el-form-item>
            </el-col>
            <el-col :xs="24" :md="12">
              <el-form-item label="标语"><el-input v-model="brand.login.slogan" /></el-form-item>
            </el-col>
            <el-col :xs="24" :md="12">
              <el-form-item label="登录卡片欢迎语"><el-input v-model="brand.login.cardWelcome" /></el-form-item>
            </el-col>
            <el-col :span="24">
              <el-form-item label="页脚版权"><el-input v-model="brand.login.footerCopyright" /></el-form-item>
            </el-col>
            <el-col :xs="24" :md="12">
              <el-form-item label="标志图网址">
                <el-input v-model="brand.login.logoUrl" placeholder="图片网址，以 https 开头" />
              </el-form-item>
            </el-col>
            <el-col :xs="24" :md="12">
              <el-form-item label="网站图标网址">
                <el-input v-model="brand.login.faviconUrl" placeholder="图标网址，以 https 开头" />
              </el-form-item>
            </el-col>
            <el-col :xs="24" :md="12">
              <el-form-item label="主题色（如 #b40010）"><el-input v-model="brand.login.themePrimary" /></el-form-item>
            </el-col>
          </el-row>
        </el-form>

        <h3 class="section-title">报名填报页</h3>
        <el-form label-position="top" class="workspace-form">
          <el-row :gutter="16">
            <el-col :xs="24" :md="12">
              <el-form-item label="页面标题"><el-input v-model="brand.apply.pageTitle" /></el-form-item>
            </el-col>
            <el-col :xs="24" :md="12">
              <el-form-item label="副标题（整页填报说明）">
                <el-input v-model="brand.apply.headerSubtitle" placeholder="展示在页面标题下方，可选用" />
              </el-form-item>
            </el-col>
          </el-row>
        </el-form>

        <h3 class="section-title">评审端</h3>
        <el-form label-position="top" class="workspace-form">
          <el-row :gutter="16">
            <el-col :xs="24" :md="12">
              <el-form-item label="页面标题"><el-input v-model="brand.review.pageTitle" /></el-form-item>
            </el-col>
            <el-col :xs="24" :md="12">
              <el-form-item label="页脚版权"><el-input v-model="brand.review.footerCopyright" /></el-form-item>
            </el-col>
          </el-row>
        </el-form>

        <h3 class="section-title">运营后台</h3>
        <el-form label-position="top" class="workspace-form">
          <el-row :gutter="16">
            <el-col :xs="24" :md="12">
              <el-form-item label="登录页标题"><el-input v-model="brand.admin.loginTitle" /></el-form-item>
            </el-col>
            <el-col :xs="24" :md="12">
              <el-form-item label="页脚版权"><el-input v-model="brand.admin.footerCopyright" /></el-form-item>
            </el-col>
            <el-col :xs="24" :md="12">
              <el-form-item label="侧栏产品名称"><el-input v-model="brand.admin.sidebarProductName" /></el-form-item>
            </el-col>
          </el-row>
        </el-form>

        <el-button native-type="button" type="primary" class="mt-compact" :loading="saving" @click="saveBasic">保存（含品牌文案）</el-button>
      </el-tab-pane>
    </el-tabs>

    <el-dialog
      v-model="schemaDialogVisible"
      :title="schemaModalPurpose === 'signup' ? '新建报名表版本' : '新建评审表版本'"
      width="480px"
      destroy-on-close
      align-center
    >
      <el-form label-position="top">
        <el-form-item label="版本名称">
          <el-input v-model="schemaNewName" autocomplete="off" />
          <p class="text-muted small mt-2 mb-0">将创建空字段列表，保存后可在「编辑」中配置。</p>
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="schemaDialogVisible = false">取消</el-button>
        <el-button type="primary" @click="confirmNewSchema">创建</el-button>
      </template>
    </el-dialog>

    <el-dialog
      v-model="editSchemaDialogVisible"
      title="编辑表单定义"
      width="min(960px, 96vw)"
      top="4vh"
      destroy-on-close
      align-center
    >
      <el-form label-position="top">
        <el-form-item label="版本名称">
          <el-input v-model="editSchemaName" autocomplete="off" />
        </el-form-item>
      </el-form>
      <el-tabs v-model="editSchemaTab">
        <el-tab-pane label="可视化设计" name="visual">
          <FormSchemaVisualEditor v-model="editSchemaVisualItems" :purpose="editSchemaPurpose" />
        </el-tab-pane>
        <el-tab-pane label="JSON 源码" name="json">
          <p class="text-muted small mb-2">
            适合粘贴备份或高级字段；切换回「可视化」时会尽量同步（仅识别常用属性）。
          </p>
          <el-input
            v-model="editSchemaJsonText"
            type="textarea"
            :rows="16"
            class="font-monospace"
            spellcheck="false"
            @blur="syncEditJsonToVisual"
          />
        </el-tab-pane>
      </el-tabs>
      <p v-if="editSchemaError" class="text-danger small mt-2 mb-0">{{ editSchemaError }}</p>
      <template #footer>
        <el-button @click="editSchemaDialogVisible = false">取消</el-button>
        <el-button type="primary" @click="saveEditedSchema">保存</el-button>
      </template>
    </el-dialog>

    <el-dialog
      v-model="trackDialogVisible"
      :title="trackEditingId ? '编辑赛道' : '新增赛道'"
      width="520px"
      destroy-on-close
      align-center
    >
      <el-form label-position="top">
        <el-form-item label="赛道编码">
          <el-input v-model="trackForm.track_code" :disabled="Boolean(trackEditingId)" autocomplete="off" />
        </el-form-item>
        <el-form-item label="标题">
          <el-input v-model="trackForm.title" autocomplete="off" />
        </el-form-item>
        <el-form-item label="简介">
          <el-input v-model="trackForm.description" type="textarea" :rows="2" />
        </el-form-item>
        <el-row :gutter="16">
          <el-col :span="12">
            <el-form-item label="排序">
              <el-input-number v-model="trackForm.sort" :step="1" controls-position="right" style="width: 100%" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="状态">
              <el-checkbox v-model="trackForm.is_enabled">启用</el-checkbox>
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <template #footer>
        <el-button @click="trackDialogVisible = false">取消</el-button>
        <el-button type="primary" @click="saveTrack">保存</el-button>
      </template>
    </el-dialog>
  </div>
</template>

<style scoped>
.competition-workspace {
  max-width: 1200px;
}

.workspace-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 16px;
  flex-wrap: wrap;
}

.workspace-title {
  margin: 0;
  font-size: 18px;
  font-weight: 600;
  color: var(--el-text-color-primary);
}

.workspace-alert {
  margin-bottom: 16px;
}

.workspace-tabs {
  margin-top: 4px;
}

.workspace-tabs--create :deep(.el-tabs__header) {
  display: none;
}

.workspace-tabs :deep(.el-tabs__content) {
  padding: 16px;
  background: var(--el-bg-color);
}

.workspace-form {
  max-width: 960px;
}

.form-hint {
  margin: 0 0 8px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--el-text-color-secondary);
}

.form-hint.pledge-hint {
  margin-bottom: 10px;
}

.pane-toolbar {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 8px;
  margin-bottom: 12px;
  flex-wrap: wrap;
}

.pane-toolbar--split {
  justify-content: space-between;
}

.pane-toolbar--wrap {
  flex-wrap: wrap;
}

.pane-toolbar-tip {
  flex: 1;
  margin: 0 12px 0 0;
  min-width: 200px;
}

.workspace-back {
  flex-shrink: 0;
}

.workspace-table {
  width: 100%;
}

.workspace-table--mb {
  margin-bottom: 16px;
}

.cell-muted {
  color: var(--el-text-color-secondary);
  font-size: 13px;
}

.section-block + .section-block {
  margin-top: 24px;
  padding-top: 8px;
  border-top: 1px solid var(--el-border-color-lighter);
}

.section-title {
  margin: 0 0 8px;
  font-size: 15px;
  font-weight: 600;
  color: var(--el-text-color-regular);
}

.subsection-label {
  margin: 12px 0 8px;
  font-size: 13px;
  font-weight: 600;
  color: var(--el-text-color-regular);
}

.json-area :deep(textarea) {
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  font-size: 13px;
}

.mt-compact {
  margin-top: 16px;
}

.mt-2 {
  margin-top: 8px;
}

.w-100 {
  width: 100%;
}

.tracks-pane {
  min-height: 120px;
}

.login-link-preview {
  margin-top: 8px;
}

.login-links-stack {
  margin-top: 10px;
  padding: 10px 12px;
  border-radius: var(--el-border-radius-base);
  background: var(--el-fill-color-light);
  border: 1px solid var(--el-border-color-lighter);
}

.login-links-stack .login-link-preview:first-of-type {
  margin-top: 0;
}

.login-link-label {
  display: block;
  margin-bottom: 4px;
  font-weight: 500;
  color: var(--el-text-color-regular);
}

.login-link-url {
  display: inline-block;
  max-width: 100%;
  word-break: break-all;
  color: var(--el-color-primary);
  text-decoration: none;
}

.login-link-url:hover {
  text-decoration: underline;
}
</style>
