<script setup lang="ts">
/**
 * 与原型 frontend/prototype/pages/admin-reviewers.html（layout.html role=admin）一致：
 * 左侧赛道配置 Tab + 右侧当前赛道评审员表（姓名/电话/账户/密码/修改|删除）。
 * 数据来源：后端 reviewers + reviewer_scopes（每场赛事由顶栏切换器选定）。
 */
import { computed, ref, watch } from 'vue'
import { storeToRefs } from 'pinia'
import { ElMessage, ElMessageBox } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
import { listTracks } from '../../../api/admin/competitions'
import { createReviewer, deleteReviewer, updateReviewer } from '../../../api/admin/reviewers'
import {
  createReviewerScope,
  deleteReviewerScope,
  fetchReviewerScopeTrackCounts,
  listReviewerScopes,
} from '../../../api/admin/reviewerScopes'
import type { CompetitionTrackRow, ReviewerScopeRow } from '../../../api/admin/types'
import { useAdminCompetitionStore } from '../../../stores/adminCompetition'
import { adminUseMock } from '../../../config/api'

const useMockBackend = computed(() => adminUseMock())

const competitionStore = useAdminCompetitionStore()
const { selectedCompetitionId } = storeToRefs(competitionStore)

const tracks = ref<CompetitionTrackRow[]>([])
const trackCounts = ref<Record<string, number>>({})
const currentTrackCode = ref<string | null>(null)
const scopeRows = ref<ReviewerScopeRow[]>([])
const shellLoading = ref(false)
const tableLoading = ref(false)

const editDialog = ref(false)
const editMode = ref<'create' | 'edit'>('create')
const saving = ref(false)
const editingReviewerId = ref<number | null>(null)

const formRef = ref<FormInstance>()
const form = ref({
  name: '',
  mobile: '',
  username: '',
  password: '',
})

const rules: FormRules = {
  name: [{ required: true, message: '请填写姓名', trigger: 'blur' }],
  username: [
    { required: true, message: '请填写账户', trigger: 'blur' },
    { pattern: /^[A-Za-z0-9._-]+$/, message: '仅字母、数字、点、横线或下划线', trigger: 'blur' },
  ],
  password: [
    {
      validator: (_rule, val, cb) => {
        if (editMode.value === 'create' && (!val || String(val).trim() === '')) {
          cb(new Error('请填写密码'))
          return
        }
        if (val && String(val).trim().length > 0 && String(val).length < 6) {
          cb(new Error('密码不少于 6 位'))
          return
        }
        cb()
      },
      trigger: 'blur',
    },
  ],
}

const currentTrackTitle = computed(
  () => tracks.value.find((t) => t.track_code === currentTrackCode.value)?.title ?? '',
)

const currentMeta = computed(() => {
  const n = currentTrackCode.value ? (trackCounts.value[currentTrackCode.value] ?? 0) : 0
  return `已配置 ${n} 名评审员`
})

async function loadTracks() {
  const cid = selectedCompetitionId.value
  shellLoading.value = true
  if (!cid) {
    tracks.value = []
    trackCounts.value = {}
    currentTrackCode.value = null
    shellLoading.value = false
    return
  }
  try {
    tracks.value = (await listTracks(cid)).filter((t) => t.is_enabled).sort((a, b) => a.sort - b.sort)
    if (!tracks.value.length) {
      currentTrackCode.value = null
      trackCounts.value = {}
      return
    }
    if (!currentTrackCode.value || !tracks.value.some((t) => t.track_code === currentTrackCode.value)) {
      currentTrackCode.value = tracks.value[0]?.track_code ?? null
    }
    trackCounts.value = await fetchReviewerScopeTrackCounts(cid)
  } catch (e) {
    tracks.value = []
    ElMessage.warning(e instanceof Error ? e.message : '赛道加载失败')
  } finally {
    shellLoading.value = false
  }
}

async function loadTable() {
  const cid = selectedCompetitionId.value
  const tc = currentTrackCode.value
  if (!cid || !tc) {
    scopeRows.value = []
    return
  }
  tableLoading.value = true
  try {
    const res = await listReviewerScopes({
      competition_id: cid,
      track_code: tc,
      page: 1,
      per_page: 500,
    })
    scopeRows.value = res.data
    trackCounts.value = await fetchReviewerScopeTrackCounts(cid)
  } catch (e) {
    scopeRows.value = []
    ElMessage.error(e instanceof Error ? e.message : '加载失败')
  } finally {
    tableLoading.value = false
  }
}

function selectTab(trackCode: string) {
  currentTrackCode.value = trackCode
}

watch(
  selectedCompetitionId,
  () => {
    currentTrackCode.value = null
    void loadTracks().then(() => loadTable())
  },
  { immediate: true },
)

watch(currentTrackCode, () => {
  void loadTable()
})

function pwdDisplay(row: ReviewerScopeRow): string {
  return row.reviewer?.password_display ?? '—'
}

function telDisplay(row: ReviewerScopeRow): string {
  const m = row.reviewer?.mobile
  return m && String(m).trim() !== '' ? String(m) : '—'
}

function openCreate() {
  editMode.value = 'create'
  editingReviewerId.value = null
  form.value = { name: '', mobile: '', username: '', password: '' }
  editDialog.value = true
}

function openEdit(row: ReviewerScopeRow) {
  editMode.value = 'edit'
  editingReviewerId.value = row.reviewer_id
  const r = row.reviewer
  form.value = {
    name: r?.name ?? '',
    mobile: r?.mobile ?? '',
    username: r?.username ?? '',
    password: '',
  }
  editDialog.value = true
}

async function submitEdit() {
  if (!formRef.value) return
  const cid = selectedCompetitionId.value
  const tc = currentTrackCode.value
  if (!cid || !tc) return

  await formRef.value.validate(async (ok) => {
    if (!ok) return
    saving.value = true
    try {
      if (editMode.value === 'create') {
        let reviewerId = 0
        try {
          const created = await createReviewer({
            name: form.value.name.trim(),
            username: form.value.username.trim(),
            password: form.value.password,
            mobile: form.value.mobile.trim() || null,
          })
          reviewerId = created.id
        } catch (e) {
          ElMessage.error(e instanceof Error ? e.message : '创建评审员失败')
          saving.value = false
          return
        }
        try {
          await createReviewerScope({
            reviewer_id: reviewerId,
            competition_id: cid,
            track_code: tc,
          })
        } catch (e) {
          try {
            await deleteReviewer(reviewerId)
          } catch {
            /* 回滚失败时仍提示绑定错误 */
          }
          ElMessage.error(e instanceof Error ? e.message : '绑定赛道失败')
          saving.value = false
          return
        }
        ElMessage.success('评审员信息已保存')
      } else {
        const rid = editingReviewerId.value!
        await updateReviewer(rid, {
          name: form.value.name.trim(),
          username: form.value.username.trim(),
          mobile: form.value.mobile.trim() || null,
          ...(form.value.password.trim() ? { password: form.value.password.trim() } : {}),
        })
        ElMessage.success('评审员信息已更新')
      }
      editDialog.value = false
      await loadTable()
      if (cid) {
        trackCounts.value = await fetchReviewerScopeTrackCounts(cid)
      }
    } catch (e) {
      ElMessage.error(e instanceof Error ? e.message : '保存失败')
    } finally {
      saving.value = false
    }
  })
}

async function onDelete(row: ReviewerScopeRow) {
  const name = row.reviewer?.name ?? '该评审员'
  try {
    await ElMessageBox.confirm(`确定移除「${name}」对本赛道的评审权限吗？（不删除全局账号）`, '确认删除', {
      type: 'warning',
      confirmButtonText: '确定删除',
      cancelButtonText: '取消',
    })
  } catch {
    return
  }
  try {
    await deleteReviewerScope(row.id)
    ElMessage.success('已移除')
    await loadTable()
    const cid = selectedCompetitionId.value
    if (cid) {
      trackCounts.value = await fetchReviewerScopeTrackCounts(cid)
    }
  } catch (e) {
    ElMessage.error(e instanceof Error ? e.message : '删除失败')
  }
}
</script>

<template>
  <div class="proto-reviewer-page admin-desktop-page">
    <div class="rev-head">
      <h5 class="section-title mb-0">评审员管理</h5>
    </div>

    <el-alert
      v-if="!selectedCompetitionId"
      type="warning"
      show-icon
      :closable="false"
      class="rev-alert"
      title="请先在顶栏「赛事切换」中选择一场赛事，再按赛道维护评审员。"
    />

    <template v-else>
      <div v-loading="shellLoading" class="reviewer-manage-shell">
        <aside class="reviewer-track-panel">
          <div class="reviewer-track-panel-title">赛道配置</div>
          <div class="reviewer-track-tabs">
            <button
              v-for="t in tracks"
              :key="t.id"
              type="button"
              class="reviewer-track-tab"
              :class="{ active: currentTrackCode === t.track_code }"
              @click="selectTab(t.track_code)"
            >
              <span class="reviewer-track-tab-name">{{ t.title }}</span>
              <span class="reviewer-track-tab-count">{{ trackCounts[t.track_code] ?? 0 }} 人</span>
            </button>
          </div>
        </aside>

        <section class="reviewer-editor-panel">
          <div class="reviewer-editor-head">
            <div>
              <h6 class="reviewer-track-title">{{ currentTrackTitle || '—' }}</h6>
              <p class="reviewer-track-meta mb-0">{{ currentMeta }}</p>
            </div>
            <div class="reviewer-editor-actions">
              <button type="button" class="prm-btn-outline" :disabled="!currentTrackCode" @click="openCreate">
                新增评审员
              </button>
            </div>
          </div>
          <div v-loading="tableLoading" class="table-responsive">
            <table v-if="currentTrackCode" class="table table-sm align-middle mb-0 reviewer-manage-table">
              <thead>
                <tr>
                  <th style="width: 18%">姓名</th>
                  <th style="width: 22%">电话</th>
                  <th style="width: 24%">账户</th>
                  <th style="width: 24%">密码</th>
                  <th class="text-end" style="width: 12%">操作</th>
                </tr>
              </thead>
              <tbody>
                <tr v-for="row in scopeRows" :key="row.id">
                  <td>{{ row.reviewer?.name ?? '—' }}</td>
                  <td>{{ telDisplay(row) }}</td>
                  <td>{{ row.reviewer?.username ?? '—' }}</td>
                  <td>{{ pwdDisplay(row) }}</td>
                  <td class="text-end reviewer-row-actions">
                    <button type="button" class="prm-btn-outline sm" @click="openEdit(row)">修改</button>
                    <button type="button" class="prm-btn-light sm" @click="onDelete(row)">删除</button>
                  </td>
                </tr>
              </tbody>
            </table>
            <div v-if="!currentTrackCode" class="empty-hint">
              {{
                tracks.length === 0
                  ? '本场暂无启用中的赛道，请先在「赛事中心」配置赛道后再维护评审员。'
                  : ''
              }}
            </div>
          </div>
          <p class="footnote">
            {{ useMockBackend ? '当前为 Mock：密码可能与原型一致以明文展示。' : '正式环境仅存密码摘要；列表中为占位符而非明文密码。' }}
          </p>
        </section>
      </div>
    </template>

    <el-dialog
      v-model="editDialog"
      :title="editMode === 'create' ? '新增评审员' : '修改评审员'"
      width="520px"
      destroy-on-close
      class="rev-dialog"
    >
      <el-form ref="formRef" :model="form" :rules="rules" label-position="top" class="rev-form">
        <el-row :gutter="12">
          <el-col :span="12">
            <el-form-item label="姓名" prop="name" required>
              <el-input v-model="form.name" maxlength="64" placeholder="必填" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="电话" prop="mobile">
              <el-input v-model="form.mobile" maxlength="20" placeholder="可选" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="账户" prop="username" required>
              <el-input v-model="form.username" maxlength="64" autocomplete="off" placeholder="必填" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item :label="editMode === 'create' ? '密码' : '密码（不修改请留空）'" prop="password">
              <el-input
                v-model="form.password"
                type="password"
                show-password
                maxlength="255"
                autocomplete="new-password"
                :placeholder="editMode === 'create' ? '必填' : '留空表示不修改'"
              />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <template #footer>
        <el-button @click="editDialog = false">取消</el-button>
        <el-button type="primary" :loading="saving" @click="submitEdit">确定</el-button>
      </template>
    </el-dialog>
  </div>
</template>

<style scoped>
.proto-reviewer-page {
  --bg: #f5f3f4;
  --text: #1f1f1f;
  --primary: #b40010;
  --primary-soft: #fbe9eb;
  --shadow-soft: 0 4px 14px rgba(46, 24, 26, 0.06);
}

.rev-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 0.75rem;
}

.section-title {
  font-size: 1.35rem;
  font-weight: 620;
  color: #2f2a2b;
}

.rev-alert {
  margin-bottom: 0.75rem;
}

.reviewer-manage-shell {
  display: grid;
  grid-template-columns: 14.5rem minmax(0, 1fr);
  gap: 0.9rem;
  align-items: start;
}

.reviewer-track-panel,
.reviewer-editor-panel {
  background: #fff;
  border: 1px solid #ece2e4;
  border-radius: 10px;
  box-shadow: var(--shadow-soft);
}

.reviewer-track-panel {
  padding: 0.72rem;
}

.reviewer-track-panel-title {
  color: #827579;
  font-size: 0.78rem;
  font-weight: 600;
  margin-bottom: 0.55rem;
}

.reviewer-track-tabs {
  display: grid;
  gap: 0.42rem;
}

.reviewer-track-tab {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  border: 1px solid transparent;
  border-radius: 8px;
  background: #fbf7f8;
  color: #3e3437;
  padding: 0.58rem 0.62rem;
  text-align: left;
  cursor: pointer;
  font: inherit;
}

.reviewer-track-tab.active {
  color: var(--primary);
  border-color: #e5bdc1;
  background: var(--primary-soft);
}

.reviewer-track-tab-name {
  font-size: 0.86rem;
  font-weight: 600;
}

.reviewer-track-tab-count {
  color: #8b7e81;
  font-size: 0.78rem;
  white-space: nowrap;
}

.reviewer-editor-panel {
  padding: 0.8rem;
}

.reviewer-editor-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 0.75rem;
  margin-bottom: 0.72rem;
}

.reviewer-editor-actions {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  flex-wrap: wrap;
  justify-content: flex-end;
}

.reviewer-track-title {
  margin: 0 0 0.18rem;
  font-size: 1rem;
  color: #322b2d;
  font-weight: 650;
}

.reviewer-track-meta {
  color: #8b7e81;
  font-size: 0.78rem;
}

.table-responsive {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

.reviewer-manage-table {
  min-width: 620px;
  width: 100%;
  border-collapse: collapse;
}

.reviewer-manage-table th {
  color: #4a4043;
  background: #faf7f7;
  font-size: 0.8rem;
  font-weight: 600;
  padding: 0.65rem 0.62rem;
  border-bottom: 1px solid #ece2e4;
}

.reviewer-manage-table td {
  padding: 0.65rem 0.62rem;
  border-bottom: 1px solid #f0e8ea;
  font-size: 0.88rem;
  vertical-align: middle;
}

.reviewer-row-actions {
  white-space: nowrap;
}

.reviewer-row-actions .prm-btn-outline + .prm-btn-light {
  margin-left: 0.35rem;
}

.prm-btn-outline,
.prm-btn-light {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0.8rem;
  border-radius: 6px;
  padding: 0.22rem 0.55rem;
  cursor: pointer;
  font-weight: 500;
  border: 1px solid #c9b8bb;
  background: #fff;
  color: var(--primary);
}

.prm-btn-outline:hover:not(:disabled) {
  background: var(--primary-soft);
}

.prm-btn-outline:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

.prm-btn-outline.sm,
.prm-btn-light.sm {
  padding: 0.18rem 0.45rem;
  font-size: 0.78rem;
}

.prm-btn-light {
  color: #3e3437;
  background: #fafafa;
  border-color: #ddd5d7;
}

.prm-btn-light:hover {
  background: #f3f0f1;
}

.empty-hint {
  padding: 1rem;
  color: #8b7e81;
  font-size: 0.86rem;
}

.footnote {
  margin: 0.55rem 0 0;
  font-size: 0.72rem;
  color: #8b7e81;
}

@media (max-width: 992px) {
  .admin-desktop-page .reviewer-manage-shell {
    grid-template-columns: 1fr;
  }

  .admin-desktop-page .reviewer-track-tabs {
    display: flex;
    gap: 0.5rem;
    overflow-x: auto;
    padding-bottom: 0.1rem;
    -webkit-overflow-scrolling: touch;
  }

  .admin-desktop-page .reviewer-track-tab {
    flex: 0 0 min(13rem, 72vw);
  }

  .reviewer-editor-head {
    flex-direction: column;
    align-items: stretch;
  }

  .reviewer-editor-actions {
    justify-content: flex-start;
  }
}
</style>
