<template>
  <ConfirmDialog
    :open="open"
    :badge="modeMeta.badge"
    :title="modeMeta.title"
    :description="modeMeta.description"
    cancel-text="取消"
    :confirm-text="modeMeta.confirmText"
    busy-text="提交中..."
    :confirm-disabled="confirmDisabled"
    :busy="busy"
    @close="closeDialog"
    @confirm="submitAction"
  >
    <div class="participants-form">
      <p v-if="permissionReason" class="participants-error" role="alert">
        {{ permissionReason }}
      </p>

      <label class="participants-search">
        <span>参与人</span>
        <div>
          <input
            v-model="candidateKeyword"
            type="search"
            placeholder="输入姓名、工号或邮箱"
            :disabled="busy"
            @keydown.enter.prevent="loadCandidates"
          />
          <button type="button" :disabled="candidateLoading || busy" @click="loadCandidates">
            <i :class="candidateLoading ? 'mdi mdi-loading mdi-spin' : 'mdi mdi-magnify'"></i>
            查询
          </button>
        </div>
      </label>

      <div class="participants-candidates" aria-label="可选审批参与人">
        <p v-if="candidateLoading">正在加载候选人...</p>
        <p v-else-if="candidateError" class="participants-error">{{ candidateError }}</p>
        <p v-else-if="!candidates.length">暂无符合条件的候选人。</p>
        <label
          v-for="candidate in candidates"
          v-else
          :key="candidate.employeeId"
          :class="{ disabled: !candidate.qualified }"
        >
          <input
            type="checkbox"
            :checked="selectedParticipantIds.includes(candidate.employeeId)"
            :disabled="!candidate.qualified || busy"
            @change="toggleParticipant(candidate, $event.target.checked)"
          />
          <span>
            <strong>{{ candidate.name || candidate.employeeNo }}</strong>
            <small>{{ candidate.employeeNo }} · {{ candidate.email }}</small>
            <em v-if="!candidate.qualified">{{ candidate.reason || '不符合当前动作资格' }}</em>
          </span>
        </label>
      </div>

      <section v-if="selectedParticipants.length" class="selected-participants">
        <header>
          <strong>{{ mode === 'add_sign' ? '顺序加签队列' : '并行会签成员' }}</strong>
          <span>{{ selectedParticipants.length }}/10</span>
        </header>
        <ol>
          <li v-for="(candidate, index) in selectedParticipants" :key="candidate.employeeId">
            <span>{{ mode === 'add_sign' ? index + 1 : '•' }}</span>
            <strong>{{ candidate.name || candidate.employeeNo }}</strong>
            <div>
              <button
                v-if="mode === 'add_sign'"
                type="button"
                aria-label="上移参与人"
                :disabled="index === 0 || busy"
                @click="moveParticipant(index, -1)"
              >↑</button>
              <button
                v-if="mode === 'add_sign'"
                type="button"
                aria-label="下移参与人"
                :disabled="index === selectedParticipants.length - 1 || busy"
                @click="moveParticipant(index, 1)"
              >↓</button>
              <button type="button" :disabled="busy" @click="removeParticipant(candidate.employeeId)">
                移除
              </button>
            </div>
          </li>
        </ol>
      </section>

      <label class="participants-reason">
        <span>{{ modeMeta.reasonLabel }} <em>必填</em></span>
        <textarea
          v-model="reason"
          maxlength="500"
          :disabled="busy"
          :placeholder="modeMeta.reasonPlaceholder"
        ></textarea>
        <small>{{ reason.length }}/500</small>
      </label>

      <p v-if="submitError" class="participants-error" role="alert">{{ submitError }}</p>
    </div>
  </ConfirmDialog>
</template>

<script setup>
import { computed, ref, watch } from 'vue'

import {
  addSignApprovalTask,
  countersignApprovalTask,
  fetchApprovalTaskCandidates
} from '../../services/approvalTasks.js'
import { resolveApprovalTaskRetryKey } from '../../views/scripts/approvalTaskRetry.js'
import ConfirmDialog from '../shared/ConfirmDialog.vue'

const MODE_METADATA = {
  add_sign: {
    badge: '顺序加签',
    title: '添加顺序审批参与人',
    description: '参与人将按所列顺序处理，全部完成后再回到原审批任务；最终规则以服务端任务编排为准。',
    confirmText: '确认加签',
    reasonLabel: '加签原因',
    reasonPlaceholder: '说明为什么需要这些参与人依次核对。'
  },
  countersign: {
    badge: '并行会签',
    title: '添加并行会签成员',
    description: '成员将并行收到审批任务，通过和退回规则由服务端会签策略统一执行。',
    confirmText: '确认会签',
    reasonLabel: '会签原因',
    reasonPlaceholder: '说明为什么需要多位参与人共同核对。'
  }
}

const props = defineProps({
  open: { type: Boolean, default: false },
  task: { type: Object, default: () => ({}) },
  mode: {
    type: String,
    default: 'add_sign',
    validator: (value) => ['add_sign', 'countersign'].includes(value)
  },
  serviceOverrides: { type: Object, default: () => ({}) }
})

const emit = defineEmits(['close', 'completed'])
const candidateKeyword = ref('')
const candidates = ref([])
const candidateLoading = ref(false)
const candidateError = ref('')
const selectedParticipantIds = ref([])
const reason = ref('')
const busy = ref(false)
const submitError = ref('')
const requestId = ref('')
const requestFingerprint = ref('')
let candidateRequestSequence = 0

const normalizedTask = computed(() => props.task?.task || props.task || {})
const taskId = computed(() => String(normalizedTask.value.id || '').trim())
const availableActions = computed(() => new Set(
  Array.isArray(normalizedTask.value.availableActions)
    ? normalizedTask.value.availableActions
    : Array.isArray(normalizedTask.value.available_actions)
      ? normalizedTask.value.available_actions
      : []
))
const modeMeta = computed(() => MODE_METADATA[props.mode] || MODE_METADATA.add_sign)
const canAct = computed(() =>
  Boolean(taskId.value)
  && Number(normalizedTask.value.version) >= 1
  && availableActions.value.has(props.mode)
)
const permissionReason = computed(() => {
  if (canAct.value) return ''
  return String(
    normalizedTask.value.readOnlyReason
    ?? normalizedTask.value.read_only_reason
    ?? '服务端未授权当前审批动作。'
  ).trim()
})
const candidateIndex = computed(() => new Map(
  candidates.value.map((candidate) => [candidate.employeeId, candidate])
))
const selectedParticipants = computed(() =>
  selectedParticipantIds.value
    .map((employeeId) => candidateIndex.value.get(employeeId))
    .filter(Boolean)
)
const confirmDisabled = computed(() =>
  !canAct.value
  || !selectedParticipantIds.value.length
  || selectedParticipantIds.value.length > 10
  || reason.value.trim().length < 2
)

watch(
  [() => props.open, taskId, () => props.mode],
  ([open]) => {
    if (!open) return
    resetForm()
    if (canAct.value) void loadCandidates()
  }
)

function resetForm() {
  candidateRequestSequence += 1
  candidateKeyword.value = ''
  candidates.value = []
  candidateError.value = ''
  selectedParticipantIds.value = []
  reason.value = ''
  submitError.value = ''
  requestId.value = ''
  requestFingerprint.value = ''
}

async function loadCandidates() {
  if (!taskId.value || !canAct.value) return
  const sequence = ++candidateRequestSequence
  candidateLoading.value = true
  candidateError.value = ''
  try {
    const service = props.serviceOverrides.fetchApprovalTaskCandidates
      || fetchApprovalTaskCandidates
    const result = await service(taskId.value, {
      action: props.mode,
      keyword: candidateKeyword.value,
      limit: 50
    })
    if (sequence !== candidateRequestSequence) return
    const nextCandidates = Array.isArray(result?.items) ? result.items : []
    const selectedCandidateMap = new Map(
      selectedParticipants.value.map((candidate) => [candidate.employeeId, candidate])
    )
    nextCandidates.forEach((candidate) => selectedCandidateMap.set(candidate.employeeId, candidate))
    candidates.value = [...selectedCandidateMap.values()]
  } catch (error) {
    if (sequence === candidateRequestSequence) {
      candidateError.value = error?.message || '审批候选人加载失败。'
    }
  } finally {
    if (sequence === candidateRequestSequence) {
      candidateLoading.value = false
    }
  }
}

function toggleParticipant(candidate, selected) {
  if (!candidate?.qualified) return
  const next = [...selectedParticipantIds.value]
  const index = next.indexOf(candidate.employeeId)
  if (selected && index < 0) {
    if (next.length >= 10) {
      submitError.value = '加签或会签参与人最多 10 名。'
      return
    }
    next.push(candidate.employeeId)
  } else if (!selected && index >= 0) {
    next.splice(index, 1)
  }
  selectedParticipantIds.value = next
  submitError.value = ''
}

function removeParticipant(employeeId) {
  selectedParticipantIds.value = selectedParticipantIds.value.filter((id) => id !== employeeId)
}

function moveParticipant(index, offset) {
  const targetIndex = index + offset
  if (targetIndex < 0 || targetIndex >= selectedParticipantIds.value.length) return
  const next = [...selectedParticipantIds.value]
  ;[next[index], next[targetIndex]] = [next[targetIndex], next[index]]
  selectedParticipantIds.value = next
}

async function submitAction() {
  if (confirmDisabled.value || busy.value) return
  const service = props.mode === 'add_sign'
    ? props.serviceOverrides.addSignApprovalTask || addSignApprovalTask
    : props.serviceOverrides.countersignApprovalTask || countersignApprovalTask
  busy.value = true
  submitError.value = ''
  try {
    const payload = {
      expectedTaskVersion: Number(normalizedTask.value.version),
      reason: reason.value.trim(),
      participantEmployeeIds: [...selectedParticipantIds.value]
    }
    const retryKey = resolveApprovalTaskRetryKey({
      action: props.mode,
      scopeId: taskId.value || 'task',
      payload,
      previousFingerprint: requestFingerprint.value,
      previousRequestId: requestId.value
    })
    requestId.value = retryKey.requestId
    requestFingerprint.value = retryKey.fingerprint
    payload.requestId = retryKey.requestId
    const mutation = await service(taskId.value, payload)
    emit('completed', { mode: props.mode, mutation })
    emit('close')
  } catch (error) {
    submitError.value = error?.message || '审批参与人操作失败。'
  } finally {
    busy.value = false
  }
}

function closeDialog() {
  if (!busy.value) emit('close')
}
</script>

<style scoped>
.participants-form,
.participants-search,
.participants-candidates,
.selected-participants,
.participants-reason {
  display: grid;
  gap: 8px;
}

.participants-search > span,
.participants-reason > span {
  color: #334155;
  font-size: 12px;
  font-weight: 850;
}

.participants-search > div {
  display: flex;
  gap: 8px;
}

.participants-search input,
.participants-reason textarea {
  width: 100%;
  min-height: 38px;
  padding: 8px 10px;
  border: 1px solid #dbe5ef;
  border-radius: 6px;
  outline: none;
}

.participants-search button,
.selected-participants button {
  padding: 0 10px;
  border: 1px solid #dbe5ef;
  border-radius: 5px;
  background: #fff;
  color: #475569;
  font-weight: 800;
}

.participants-candidates {
  max-height: 190px;
  overflow-y: auto;
}

.participants-candidates > p {
  margin: 0;
  padding: 10px;
  border-radius: 6px;
  background: #f8fafc;
  color: #64748b;
  font-size: 12px;
}

.participants-candidates label {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  padding: 8px;
  border: 1px solid #e2e8f0;
  border-radius: 6px;
}

.participants-candidates label.disabled {
  background: #f8fafc;
  opacity: 0.7;
}

.participants-candidates label strong,
.participants-candidates label small,
.participants-candidates label em {
  display: block;
}

.participants-candidates label strong {
  color: #0f172a;
  font-size: 13px;
}

.participants-candidates label small {
  margin-top: 2px;
  color: #64748b;
}

.participants-candidates label em {
  margin-top: 3px;
  color: #b45309;
  font-size: 11px;
  font-style: normal;
}

.selected-participants {
  padding: 10px;
  border: 1px solid #dbe5ef;
  border-radius: 6px;
  background: #f8fafc;
}

.selected-participants header,
.selected-participants li,
.selected-participants li div {
  display: flex;
  align-items: center;
}

.selected-participants header {
  justify-content: space-between;
}

.selected-participants header strong,
.selected-participants header span {
  color: #334155;
  font-size: 12px;
}

.selected-participants ol {
  display: grid;
  gap: 6px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.selected-participants li {
  gap: 8px;
  padding: 6px 8px;
  border-radius: 5px;
  background: #fff;
}

.selected-participants li > span {
  min-width: 20px;
  color: var(--theme-primary-active);
  font-weight: 850;
}

.selected-participants li > strong {
  flex: 1;
  color: #0f172a;
  font-size: 12px;
}

.selected-participants li div {
  gap: 4px;
}

.selected-participants button {
  min-height: 26px;
  font-size: 11px;
}

.participants-reason textarea {
  min-height: 80px;
  resize: vertical;
}

.participants-reason small {
  color: #64748b;
  font-size: 11px;
}

.participants-reason em {
  color: #b91c1c;
  font-style: normal;
}

.participants-error {
  margin: 0;
  padding: 9px 10px;
  border: 1px solid #fecaca;
  border-radius: 6px;
  background: #fef2f2;
  color: #b91c1c;
  font-size: 12px;
}
</style>
