import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'

import ConfirmDialog from '../../components/shared/ConfirmDialog.vue'
import EnterpriseSelect from '../../components/shared/EnterpriseSelect.vue'
import TableLoadingState from '../../components/shared/TableLoadingState.vue'
import TableEmptyState from '../../components/shared/TableEmptyState.vue'
import { useToast } from '../../composables/useToast.js'
import {
  disableEmployee,
  downloadEmployeeImportTemplate,
  enableEmployee,
  exportEmployees,
  fetchEmployeeDetail,
  fetchEmployeeMeta,
  fetchEmployees,
  importEmployees,
  updateEmployee
} from '../../services/employees.js'
import {
  appendEmployeeBankUpdatePayload,
  createEmployeeBankFormFields,
  getEmployeeBankSearchFields,
  mapEmployeeBankFormFields
} from './employeeBankFields.js'

const DEFAULT_STATUS_TABS = ['全部员工', '在职', '试用中', '停用']
const FALLBACK_ROLE_OPTIONS = [
  {
    id: 'manager',
    code: 'manager',
    label: '管理员',
    desc: '可以维护员工档案、组织结构和角色权限。'
  },
  {
    id: 'finance',
    code: 'finance',
    label: '财务人员',
    desc: '可以处理复核、查看财务知识与风险校验结果。'
  },
  {
    id: 'approver',
    code: 'approver',
    label: '审批负责人',
    desc: '可以处理单据中心中的待审单据。'
  },
  {
    id: 'executive',
    code: 'executive',
    label: '高级财务人员',
    desc: '可以查看跨部门预算、经营看板与关键财务审批结果。'
  },
  {
    id: 'budget_monitor',
    code: 'budget_monitor',
    label: '预算监控员',
    desc: '可以查看本部门预算执行、预警和占用情况。'
  },
  {
    id: 'user',
    code: 'user',
    label: '使用者',
    desc: '可以发起费用申请、报销、查看个人单据和使用 AI 助手。'
  }
]

function createEmployeeForm() {
  return {
    name: '',
    employeeNo: '',
    gender: '',
    age: '',
    birthDate: '',
    phone: '',
    email: '',
    joinDate: '',
    location: '',
    position: '',
    grade: '',
    department: '',
    organizationUnitCode: '',
    manager: '',
    managerEmployeeNo: '',
    financeOwner: '',
    costCenter: '',
    ...createEmployeeBankFormFields(),
    roleCodes: [],
    password: ''
  }
}

function isPlaceholderManagerName(name) {
  const normalized = normalizeText(name)
  return !normalized || normalized === 'CEO' || normalized === '无'
}

function resolveManagerEmployeeNo(employee, roster = []) {
  const fromApi = normalizeText(employee?.managerEmployeeNo)
  if (fromApi) {
    return fromApi
  }

  const managerName = normalizeText(employee?.manager)
  if (isPlaceholderManagerName(managerName)) {
    return ''
  }

  const matches = roster.filter((item) => normalizeText(item.name) === managerName)
  if (matches.length === 1) {
    return matches[0].employeeNo
  }

  return ''
}

function enrichEmployeeRecord(employee, roster = []) {
  if (!employee) {
    return employee
  }

  const managerEmployeeNo = resolveManagerEmployeeNo(employee, roster)
  if (!managerEmployeeNo || managerEmployeeNo === employee.managerEmployeeNo) {
    return employee
  }

  return {
    ...employee,
    managerEmployeeNo
  }
}

function mergeEmployeeRecords(listItem, detailItem, roster = []) {
  if (!listItem && !detailItem) {
    return null
  }

  if (!listItem) {
    return enrichEmployeeRecord(detailItem, roster)
  }

  if (!detailItem) {
    return enrichEmployeeRecord(listItem, roster)
  }

  const managerEmployeeNo =
    normalizeText(detailItem.managerEmployeeNo) ||
    normalizeText(listItem.managerEmployeeNo) ||
    resolveManagerEmployeeNo(detailItem, roster) ||
    resolveManagerEmployeeNo(listItem, roster)

  const history =
    Array.isArray(detailItem.history) && detailItem.history.length
      ? detailItem.history
      : listItem.history || []

  const permissions =
    Array.isArray(detailItem.permissions) && detailItem.permissions.length
      ? detailItem.permissions
      : listItem.permissions || []

  return enrichEmployeeRecord(
    {
      ...listItem,
      ...detailItem,
      manager: detailItem.manager || listItem.manager,
      managerEmployeeNo: managerEmployeeNo || null,
      history,
      permissions,
      roleCodes: detailItem.roleCodes?.length ? detailItem.roleCodes : listItem.roleCodes,
      roles: detailItem.roles?.length ? detailItem.roles : listItem.roles,
      organization: detailItem.organization || listItem.organization,
      department: detailItem.department || listItem.department
    },
    roster
  )
}

function buildEmployeeForm(employee, roster = []) {
  if (!employee) {
    return createEmployeeForm()
  }

  const birthDate = employee.birthDate || ''
  const managerName = employee.manager || ''
  const managerEmployeeNo = resolveManagerEmployeeNo(employee, roster)

  return {
    name: employee.name || '',
    employeeNo: employee.employeeNo || '',
    gender: employee.gender || '',
    age:
      employee.age !== null && employee.age !== undefined && employee.age !== ''
        ? String(employee.age)
        : calculateAgeFromDate(birthDate),
    birthDate,
    phone: employee.phone || '',
    email: employee.email || '',
    joinDate: employee.joinDate || '',
    location: employee.location || '',
    position: employee.position || '',
    grade: employee.grade || '',
    department: resolveOrganizationUnitName(employee),
    organizationUnitCode: resolveOrganizationUnitCode(employee),
    manager: managerName,
    managerEmployeeNo,
    financeOwner: employee.financeOwner || '',
    costCenter: employee.costCenter || '',
    ...mapEmployeeBankFormFields(employee),
    roleCodes: [...(employee.roleCodes || [])],
    password: ''
  }
}

function normalizeText(value) {
  return String(value || '').trim()
}

function normalizeNullableText(value) {
  const text = normalizeText(value)
  return text || null
}

function isValidEmail(value) {
  const normalized = normalizeText(value)
  if (!normalized) {
    return false
  }

  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/u.test(normalized)
}

function isValidIsoDate(value) {
  const normalized = normalizeText(value)
  if (!normalized) {
    return false
  }

  if (!/^\d{4}-\d{2}-\d{2}$/u.test(normalized)) {
    return false
  }

  const [yearText, monthText, dayText] = normalized.split('-')
  const year = Number.parseInt(yearText, 10)
  const month = Number.parseInt(monthText, 10)
  const day = Number.parseInt(dayText, 10)

  if ([year, month, day].some((item) => Number.isNaN(item))) {
    return false
  }

  const parsed = new Date(year, month - 1, day)
  if (Number.isNaN(parsed.getTime())) {
    return false
  }

  return (
    parsed.getFullYear() === year &&
    parsed.getMonth() === month - 1 &&
    parsed.getDate() === day
  )
}

function sameValues(left, right) {
  if (left.length !== right.length) {
    return false
  }

  return left.every((value, index) => value === right[index])
}

function padDatePart(value) {
  return String(Number(value)).padStart(2, '0')
}

function formatEmployeeHistoryTime(value) {
  const raw = normalizeText(value)
  if (!raw) {
    return ''
  }

  const chineseMatched = raw.match(
    /^(\d{4})年(\d{1,2})月(\d{1,2})日(\d{1,2})时(\d{1,2})分(?:\d{1,2}秒)?$/
  )
  if (chineseMatched) {
    const [, year, month, day, hour, minute] = chineseMatched
    return `${year}-${padDatePart(month)}-${padDatePart(day)} ${padDatePart(hour)}:${padDatePart(minute)}`
  }

  const isoMatched = raw.match(/^(\d{4})-(\d{1,2})-(\d{1,2})(?:[ T](\d{1,2}):(\d{1,2}))?/)
  if (isoMatched) {
    const [, year, month, day, hour = '0', minute = '0'] = isoMatched
    return `${year}-${padDatePart(month)}-${padDatePart(day)} ${padDatePart(hour)}:${padDatePart(minute)}`
  }

  return raw.replace(/(\d{1,2}分)\d{1,2}秒$/, '$1')
}

function resolveOrganizationUnitCode(employee) {
  return normalizeText(employee?.organization?.code)
}

function resolveOrganizationUnitName(employee) {
  return normalizeText(employee?.department) || normalizeText(employee?.organization?.name)
}

function captureEmployeeDetailSnapshot(form) {
  return {
    roleCodes: [...(form.roleCodes || [])].sort(),
    organizationUnitCode: normalizeText(form.organizationUnitCode) || ''
  }
}

function resolveOrganizationOptions(metaOrganizations) {
  if (!Array.isArray(metaOrganizations) || !metaOrganizations.length) {
    return []
  }

  return metaOrganizations
    .map((item) => ({
      id: item.id,
      code: item.code,
      name: item.name,
      unitType: item.unitType,
      label: `${item.name}（${item.code}）`
    }))
    .sort((a, b) => a.name.localeCompare(b.name, 'zh-CN'))
}

function calculateAgeFromDate(dateString) {
  if (!dateString) {
    return ''
  }

  const birthDate = new Date(`${dateString}T00:00:00`)
  if (Number.isNaN(birthDate.getTime())) {
    return ''
  }

  const today = new Date()
  let age = today.getFullYear() - birthDate.getFullYear()
  const hasBirthdayPassed =
    today.getMonth() > birthDate.getMonth() ||
    (today.getMonth() === birthDate.getMonth() && today.getDate() >= birthDate.getDate())

  if (!hasBirthdayPassed) {
    age -= 1
  }

  return age >= 0 ? String(age) : ''
}

function calculateBirthDateFromAge(ageValue, existingBirthDate = '') {
  const age = Number.parseInt(String(ageValue ?? '').trim(), 10)
  if (Number.isNaN(age) || age < 0 || age > 120) {
    return existingBirthDate || ''
  }

  const today = new Date()
  let month = '01'
  let day = '01'

  if (existingBirthDate && isValidIsoDate(existingBirthDate)) {
    const [, monthText, dayText] = existingBirthDate.split('-')
    month = monthText
    day = dayText
  }

  let birthYear = today.getFullYear() - age
  let candidate = `${birthYear}-${month}-${day}`

  if (Number(calculateAgeFromDate(candidate)) > age) {
    birthYear -= 1
    candidate = `${birthYear}-${month}-${day}`
  }

  return candidate
}

function matchKeyword(employee, keyword) {
  if (!keyword) {
    return true
  }

  const fields = [
    employee.name,
    employee.employeeNo,
    employee.department,
    employee.position,
    employee.email,
    employee.manager,
    employee.financeOwner,
    ...getEmployeeBankSearchFields(employee),
    employee.syncState
  ]

  const roles = Array.isArray(employee.roles) ? employee.roles : []

  const haystack = [...fields, ...roles]
    .map((val) => String(val || '').trim())
    .filter(Boolean)
    .join(' ')
    .toLowerCase()

  return haystack.includes(keyword)
}

function uniqueSorted(values) {
  return [...new Set(values.filter(Boolean))].sort((left, right) => {
    return String(left).localeCompare(String(right), 'zh-CN')
  })
}

function resolveRoleOptions(metaRoles, employees) {
  const options = Array.isArray(metaRoles) && metaRoles.length ? metaRoles : FALLBACK_ROLE_OPTIONS
  const existingLabels = new Set(options.map((item) => item.label))
  const unknownRoles = uniqueSorted(employees.flatMap((item) => item.roles || [])).filter(
    (label) => !existingLabels.has(label)
  )

  return [
    ...options,
    ...unknownRoles.map((label) => ({
      id: label,
      code: label,
      label,
      desc: '该角色来自当前员工数据。'
    }))
  ]
}

function buildStatusTabs(employees) {
  return DEFAULT_STATUS_TABS.map((label) => ({
    label,
    count:
      label === '全部员工'
        ? employees.length
        : employees.filter((item) => item.status === label).length
  }))
}

function buildEmployeeSummary(employees) {
  return {
    total: employees.length,
    active: employees.filter((item) => item.status === '在职').length,
    onboarding: employees.filter((item) => item.status === '试用中').length,
    disabled: employees.filter((item) => item.status === '停用').length,
    followUp: employees.filter((item) => item.syncState !== '已同步').length,
    departments: uniqueSorted(employees.map((item) => item.department)).length
  }
}

export default {
  name: 'EmployeeManagementView',
  components: {
    ConfirmDialog,
    EnterpriseSelect,
    TableLoadingState,
    TableEmptyState
  },
  emits: ['overview-change'],
  setup(_, { emit }) {
    const { toast } = useToast()
    const activeTab = ref(DEFAULT_STATUS_TABS[0])
    const selectedEmployee = ref(null)
    const employeeForm = ref(createEmployeeForm())
    const roleOptions = ref([...FALLBACK_ROLE_OPTIONS])
    const employees = ref([])
    const searchKeyword = ref('')
    const selectedDepartment = ref('')
    const selectedGrade = ref('')
    const selectedRole = ref('')
    const activeFilterPopover = ref('')
    const currentPage = ref(1)
    const pageSize = ref(10)
    const pageSizes = [10, 20, 50]
    const pageSizeOptions = pageSizes.map((size) => ({ label: `${size} 条/页`, value: size }))
    const actionState = ref('')
    const loading = ref(false)
    const errorMessage = ref('')
    const disableDialogOpen = ref(false)
    const importFileInput = ref(null)
    const pendingImportFile = ref(null)
    const importConfirmDialogOpen = ref(false)
    const importErrorDialogOpen = ref(false)
    const importErrors = ref([])
    const importResultMessage = ref('')
    const managerPickerOpen = ref(false)
    const managerSearchKeyword = ref('')
    const departmentPickerOpen = ref(false)
    const departmentSearchKeyword = ref('')
    const organizationUnitOptions = ref([])
    const employeeDetailSnapshot = ref(null)

    const tabs = computed(() => buildStatusTabs(employees.value))
    const employeeSummary = computed(() => buildEmployeeSummary(employees.value))
    const roleCount = computed(() => employeeForm.value.roleCodes.length)
    const selectedRoleLabels = computed(() =>
      roleOptions.value
        .filter((role) => employeeForm.value.roleCodes.includes(role.code))
        .map((role) => role.label)
    )
    const actionBusy = computed(
      () =>
        actionState.value === 'save' ||
        actionState.value === 'disable' ||
        actionState.value === 'import' ||
        actionState.value === 'export'
    )
    const importExportBusy = computed(
      () => actionState.value === 'import' || actionState.value === 'export'
    )
    const disableActionDisabled = computed(() => actionBusy.value || !selectedEmployee.value)
    const selectedEmployeeDisabled = computed(() => selectedEmployee.value?.status === '停用')
    const statusActionCopy = computed(() => {
      if (selectedEmployeeDisabled.value) {
        return {
          buttonLabel: actionState.value === 'disable' ? '启用中...' : '启用账号',
          buttonIcon: 'mdi mdi-account-check-outline',
          badge: '启用账号',
          badgeTone: 'info',
          title: `确认启用 ${selectedEmployee.value?.name || '该员工'} 的账号吗？`,
          description: '启用后该员工将恢复登录能力，并重新获得个人业务入口访问权限。',
          confirmText: '确认启用',
          busyText: '启用中...',
          confirmTone: 'primary',
          confirmIcon: 'mdi mdi-account-check-outline',
          successMessage: '员工账号已启用。',
          failureMessage: '启用账号失败，请稍后重试。'
        }
      }

      return {
        buttonLabel: actionState.value === 'disable' ? '停用中...' : '停用账号',
        buttonIcon: 'mdi mdi-account-cancel-outline',
        badge: '停用账号',
        badgeTone: 'warning',
        title: `确认停用 ${selectedEmployee.value?.name || '该员工'} 的账号吗？`,
        description: '停用后该员工将无法继续登录系统，相关个人操作入口也会立即失效。',
        confirmText: '确认停用',
        busyText: '停用中...',
        confirmTone: 'danger',
        confirmIcon: 'mdi mdi-account-cancel-outline',
        successMessage: '员工账号已停用。',
        failureMessage: '停用账号失败，请稍后重试。'
      }
    })

    const departmentOptions = computed(() =>
      uniqueSorted(employees.value.map((item) => item.department))
    )

    const gradeOptions = computed(() => uniqueSorted(employees.value.map((item) => item.grade)))

    const roleFilterOptions = computed(() =>
      uniqueSorted(
        roleOptions.value.map((item) => item.label).concat(
          employees.value.flatMap((item) => item.roles || [])
        )
      )
    )

    const managerOptions = computed(() => {
      const currentId = selectedEmployee.value?.id
      return employees.value.filter((item) => item.id !== currentId)
    })

    const filteredManagerOptions = computed(() => {
      const keyword = managerSearchKeyword.value.trim().toLowerCase()
      if (!keyword) {
        return managerOptions.value.slice(0, 20)
      }

      return managerOptions.value
        .filter((item) => {
          const haystack = [
            item.name,
            item.employeeNo,
            item.department,
            item.position,
            item.email
          ]
            .filter(Boolean)
            .join(' ')
            .toLowerCase()

          return haystack.includes(keyword)
        })
        .slice(0, 20)
    })

    const managerDisplayLabel = computed(() => {
      const managerNo = normalizeText(employeeForm.value.managerEmployeeNo)
      const managerName = normalizeText(employeeForm.value.manager)

      if (managerNo) {
        const matched =
          managerOptions.value.find((item) => item.employeeNo === managerNo) ||
          employees.value.find((item) => item.employeeNo === managerNo)

        if (matched) {
          return `${matched.name}（${matched.employeeNo}）`
        }

        return managerName ? `${managerName}（${managerNo}）` : managerNo
      }

      if (!isPlaceholderManagerName(managerName)) {
        return managerName
      }

      return '未设置直属上级'
    })

    const filteredDepartmentOptions = computed(() => {
      const keyword = departmentSearchKeyword.value.trim().toLowerCase()
      const options = organizationUnitOptions.value

      if (!keyword) {
        return options.slice(0, 20)
      }

      return options
        .filter((item) => {
          const haystack = [item.name, item.code, item.unitType, item.label]
            .filter(Boolean)
            .join(' ')
            .toLowerCase()

          return haystack.includes(keyword)
        })
        .slice(0, 20)
    })

    const departmentDisplayLabel = computed(() => {
      const code = normalizeText(employeeForm.value.organizationUnitCode)
      const name = normalizeText(employeeForm.value.department)

      if (code) {
        const matched = organizationUnitOptions.value.find((item) => item.code === code)
        if (matched) {
          return matched.label
        }

        return name ? `${name}（${code}）` : code
      }

      return name || '请选择所属部门'
    })

    const filteredEmployees = computed(() => {
      const keyword = searchKeyword.value.trim().toLowerCase()

      return employees.value.filter((item) => {
        const matchesStatus =
          activeTab.value === '全部员工' ? true : item.status === activeTab.value
        const matchesDepartment = selectedDepartment.value
          ? item.department === selectedDepartment.value
          : true
        const matchesGrade = selectedGrade.value ? item.grade === selectedGrade.value : true
        const matchesRole = selectedRole.value
          ? (item.roles || []).includes(selectedRole.value)
          : true

        return (
          matchesStatus &&
          matchesDepartment &&
          matchesGrade &&
          matchesRole &&
          matchKeyword(item, keyword)
        )
      })
    })

    const totalCount = computed(() => filteredEmployees.value.length)
    const totalPages = computed(() => Math.max(1, Math.ceil(totalCount.value / pageSize.value)))

    const visibleEmployees = computed(() => {
      const start = (currentPage.value - 1) * pageSize.value
      return filteredEmployees.value.slice(start, start + pageSize.value)
    })

    const activeFilterTokens = computed(() => {
      const tokens = []

      if (selectedDepartment.value) {
        tokens.push(`部门：${selectedDepartment.value}`)
      }

      if (selectedGrade.value) {
        tokens.push(`职级：${selectedGrade.value}`)
      }

      if (selectedRole.value) {
        tokens.push(`角色：${selectedRole.value}`)
      }

      if (searchKeyword.value.trim()) {
        tokens.push(`搜索：${searchKeyword.value.trim()}`)
      }

      return tokens
    })

    const hasActiveFilters = computed(() => activeFilterTokens.value.length > 0)
    const hasEmployeeFilters = computed(() => {
      return activeTab.value !== DEFAULT_STATUS_TABS[0] || hasActiveFilters.value
    })
    const employeeEmptyState = computed(() => {
      if (!employees.value.length) {
        return {
          eyebrow: '员工台账',
          title: '员工目录暂时还是空的',
          desc: '当前环境还没有同步任何员工档案。完成目录接入后，这里会展示员工基础信息、角色和状态。',
          icon: 'mdi mdi-account-group-outline',
          actionLabel: '重新加载',
          actionIcon: 'mdi mdi-refresh',
          tone: 'sky',
          artLabel: 'PEOPLE',
          tips: ['支持按部门、职级和角色统一维护', '点击列表行即可进入档案和权限详情']
        }
      }

      return {
        eyebrow: hasEmployeeFilters.value ? '筛选结果为空' : '员工状态为空',
        title: hasEmployeeFilters.value ? '当前条件下没有匹配员工' : `“${activeTab.value}”里暂时没有员工`,
        desc: hasEmployeeFilters.value
          ? '可以切回“全部员工”，或者清空关键词、部门、职级和角色条件后再试。'
          : '这个状态标签下目前还没有记录，你可以切换到其他状态继续查看。',
        icon: hasEmployeeFilters.value ? 'mdi mdi-account-search-outline' : 'mdi mdi-badge-account-horizontal-outline',
        actionLabel: hasEmployeeFilters.value ? '清空筛选' : '查看全部员工',
        actionIcon: hasEmployeeFilters.value ? 'mdi mdi-filter-remove-outline' : 'mdi mdi-format-list-bulleted',
        tone: hasEmployeeFilters.value ? 'primary' : 'slate',
        artLabel: hasEmployeeFilters.value ? 'FILTER' : 'STATUS',
        tips: hasEmployeeFilters.value
          ? ['关键词、部门、职级和角色条件会叠加生效', '也可以直接搜索姓名、工号或岗位']
          : ['员工状态统计会按真实目录数据自动更新', '停用员工仍会保留在台账中便于追溯']
      }
    })

    watch(
      employeeSummary,
      (summary) => {
        emit('overview-change', summary)
      },
      { immediate: true }
    )

    function syncFormFromEmployee(employee) {
      if (!employee) {
        employeeForm.value = createEmployeeForm()
        employeeDetailSnapshot.value = null
        return
      }

      const preservedPassword = employeeForm.value.password
      employeeForm.value = buildEmployeeForm(employee, employees.value)
      employeeForm.value.password = preservedPassword
      employeeDetailSnapshot.value = captureEmployeeDetailSnapshot(employeeForm.value)
    }

    watch(
      () => selectedEmployee.value?.id ?? null,
      (employeeId, previousId) => {
        if (!employeeId) {
          syncFormFromEmployee(null)
          return
        }

        if (employeeId === previousId) {
          return
        }

        syncFormFromEmployee(selectedEmployee.value)
      },
      { immediate: true }
    )

    watch(employees, () => {
      if (!selectedEmployee.value?.id) {
        return
      }

      const preserved = selectedEmployee.value
      const fromList = employees.value.find((item) => item.id === preserved.id)
      if (!fromList) {
        return
      }

      selectedEmployee.value = mergeEmployeeRecords(fromList, preserved, employees.value)
    })

    const hasManagerAssignment = computed(() => {
      return (
        Boolean(normalizeText(employeeForm.value.managerEmployeeNo)) ||
        !isPlaceholderManagerName(employeeForm.value.manager)
      )
    })

    const recentEmployeeHistory = computed(() => {
      const history = selectedEmployee.value?.history
      if (!Array.isArray(history)) {
        return []
      }

      return history.slice(0, 5)
    })

    watch(filteredEmployees, () => {
      currentPage.value = 1
    })

    function resetFilters() {
      searchKeyword.value = ''
      selectedDepartment.value = ''
      selectedGrade.value = ''
      selectedRole.value = ''
      activeTab.value = DEFAULT_STATUS_TABS[0]
      activeFilterPopover.value = ''
    }

    function handleEmployeeEmptyAction() {
      if (!employees.value.length) {
        loadEmployees().catch(() => {})
        return
      }

      resetFilters()
    }

    function changePageSize(size) {
      pageSize.value = size
      currentPage.value = 1
    }

    function toggleFilterPopover(name) {
      activeFilterPopover.value = activeFilterPopover.value === name ? '' : name
    }

    function closeFilterPopover() {
      activeFilterPopover.value = ''
    }

    function selectFilter(name, value) {
      if (name === 'department') {
        selectedDepartment.value = value
      }

      if (name === 'grade') {
        selectedGrade.value = value
      }

      if (name === 'role') {
        selectedRole.value = value
      }

      closeFilterPopover()
    }

    function handleDocumentClick(event) {
      const target = event.target

      if (!(target instanceof Element)) {
        closeFilterPopover()
        return
      }

      if (!target.closest('.picker-filter')) {
        closeFilterPopover()
      }

      if (!target.closest('.manager-picker')) {
        closeManagerPicker()
      }

      if (!target.closest('.department-picker')) {
        closeDepartmentPicker()
      }

      if (
        target.closest('.picker-filter') ||
        target.closest('.manager-picker') ||
        target.closest('.department-picker')
      ) {
        return
      }
    }

    function toggleDepartmentPicker() {
      departmentPickerOpen.value = !departmentPickerOpen.value
      if (!departmentPickerOpen.value) {
        departmentSearchKeyword.value = ''
      }
    }

    function closeDepartmentPicker() {
      departmentPickerOpen.value = false
      departmentSearchKeyword.value = ''
    }

    function selectDepartment(option) {
      if (!option) {
        return
      }

      employeeForm.value.organizationUnitCode = option.code
      employeeForm.value.department = option.name
      closeDepartmentPicker()
    }

    function resolveDepartmentSelectionFromKeyword() {
      const keyword = normalizeText(departmentSearchKeyword.value)
      if (!keyword || normalizeText(employeeForm.value.organizationUnitCode)) {
        return
      }

      const exactMatches = organizationUnitOptions.value.filter(
        (item) => item.code === keyword || item.name === keyword
      )

      if (exactMatches.length === 1) {
        selectDepartment(exactMatches[0])
      }
    }

    function toggleManagerPicker() {
      managerPickerOpen.value = !managerPickerOpen.value
      if (!managerPickerOpen.value) {
        managerSearchKeyword.value = ''
      }
    }

    function closeManagerPicker() {
      managerPickerOpen.value = false
      managerSearchKeyword.value = ''
    }

    function selectManager(option) {
      if (!option) {
        employeeForm.value.managerEmployeeNo = ''
        employeeForm.value.manager = ''
        closeManagerPicker()
        return
      }

      employeeForm.value.managerEmployeeNo = option.employeeNo
      employeeForm.value.manager = option.name
      closeManagerPicker()
    }

    function resolveManagerSelectionFromKeyword() {
      const keyword = normalizeText(managerSearchKeyword.value)
      if (!keyword || normalizeText(employeeForm.value.managerEmployeeNo)) {
        return
      }

      const exactMatches = managerOptions.value.filter(
        (item) => item.employeeNo === keyword || item.name === keyword
      )

      if (exactMatches.length === 1) {
        selectManager(exactMatches[0])
      }
    }

    function openEmployeeDetail(employee) {
      selectedEmployee.value = employee
    }

    function closeEmployeeDetail() {
      selectedEmployee.value = null
      employeeForm.value = createEmployeeForm()
      actionState.value = ''
      closeManagerPicker()
      closeDepartmentPicker()
    }

    function syncAgeFromBirthDate() {
      employeeForm.value.age = calculateAgeFromDate(employeeForm.value.birthDate)
    }

    function syncBirthDateFromAge() {
      const ageText = normalizeText(employeeForm.value.age)
      if (!ageText) {
        return
      }

      employeeForm.value.birthDate = calculateBirthDateFromAge(
        ageText,
        employeeForm.value.birthDate
      )
    }

    function buildUpdatePayload() {
      const current = selectedEmployee.value
      const form = employeeForm.value
      const payload = {}

      if (!current) {
        return payload
      }

      const nextName = normalizeText(form.name)
      if (nextName && nextName !== current.name) {
        payload.name = nextName
      }

      const nextGender = normalizeNullableText(form.gender)
      if (nextGender !== (current.gender || null)) {
        payload.gender = nextGender
      }

      const nextBirthDate = normalizeNullableText(form.birthDate)
      if (nextBirthDate !== (current.birthDate || null)) {
        payload.birth_date = nextBirthDate
      }

      const nextPhone = normalizeNullableText(form.phone)
      if (nextPhone !== (current.phone || null)) {
        payload.phone = nextPhone
      }

      const nextEmail = normalizeText(form.email)
      if (nextEmail && nextEmail !== current.email) {
        payload.email = nextEmail
      }

      const nextJoinDate = normalizeNullableText(form.joinDate)
      if (nextJoinDate !== (current.joinDate || null)) {
        payload.join_date = nextJoinDate
      }

      const nextLocation = normalizeNullableText(form.location)
      if (nextLocation !== (current.location || null)) {
        payload.location = nextLocation
      }

      const nextPosition = normalizeText(form.position)
      if (nextPosition && nextPosition !== current.position) {
        payload.position = nextPosition
      }

      const nextGrade = normalizeText(form.grade)
      if (nextGrade && nextGrade !== current.grade) {
        payload.grade = nextGrade
      }

      const nextOrganizationCode = normalizeText(form.organizationUnitCode)
      const currentOrganizationCode =
        normalizeText(employeeDetailSnapshot.value?.organizationUnitCode) ||
        resolveOrganizationUnitCode(current) ||
        ''
      if (nextOrganizationCode !== currentOrganizationCode) {
        payload.organization_unit_code = nextOrganizationCode
      }

      const nextFinanceOwner = normalizeNullableText(form.financeOwner)
      if (nextFinanceOwner !== (current.financeOwner || null)) {
        payload.finance_owner_name = nextFinanceOwner
      }

      const nextCostCenter = normalizeNullableText(form.costCenter)
      if (nextCostCenter !== (current.costCenter || null)) {
        payload.cost_center = nextCostCenter
      }

      appendEmployeeBankUpdatePayload(payload, form, current, normalizeNullableText)

      const nextManagerEmployeeNo = normalizeNullableText(form.managerEmployeeNo)
      const currentManagerEmployeeNo =
        normalizeNullableText(current.managerEmployeeNo) ||
        resolveManagerEmployeeNo(current, employees.value) ||
        null
      if (nextManagerEmployeeNo !== currentManagerEmployeeNo) {
        payload.manager_employee_no = nextManagerEmployeeNo || ''
      }

      const nextRoleCodes = [...form.roleCodes].sort()
      const currentRoleCodes = [...(employeeDetailSnapshot.value?.roleCodes || current.roleCodes || [])].sort()
      if (!sameValues(nextRoleCodes, currentRoleCodes)) {
        payload.role_codes = [...form.roleCodes]
      }

      const nextPassword = normalizeText(form.password)
      if (nextPassword) {
        payload.password = nextPassword
      }

      return payload
    }

    async function saveEmployeeChanges() {
      if (!selectedEmployee.value || actionBusy.value) {
        return
      }

      if (!normalizeText(employeeForm.value.name)) {
        toast('员工姓名不能为空。')
        return
      }

      if (!normalizeText(employeeForm.value.email)) {
        toast('邮箱不能为空。')
        return
      }

      if (!isValidEmail(employeeForm.value.email)) {
        toast('请输入有效的邮箱地址。')
        return
      }

      if (!normalizeText(employeeForm.value.position)) {
        toast('岗位不能为空。')
        return
      }

      if (!normalizeText(employeeForm.value.grade)) {
        toast('职级不能为空。')
        return
      }

      const ageText = normalizeText(employeeForm.value.age)
      if (ageText) {
        const age = Number.parseInt(ageText, 10)
        if (Number.isNaN(age) || age < 0 || age > 120) {
          toast('年龄请输入 0 到 120 之间的整数。')
          return
        }
        syncBirthDateFromAge()
      }

      const birthDate = normalizeNullableText(employeeForm.value.birthDate)
      if (birthDate && !isValidIsoDate(birthDate)) {
        toast('出生日期格式不正确，请使用 YYYY-MM-DD。')
        return
      }

      const joinDate = normalizeNullableText(employeeForm.value.joinDate)
      if (joinDate && !isValidIsoDate(joinDate)) {
        toast('入职日期格式不正确，请使用 YYYY-MM-DD。')
        return
      }

      if (normalizeText(employeeForm.value.password) && normalizeText(employeeForm.value.password).length < 5) {
        toast('员工密码至少需要 5 位。')
        return
      }

      resolveManagerSelectionFromKeyword()
      resolveDepartmentSelectionFromKeyword()

      if (!normalizeText(employeeForm.value.organizationUnitCode)) {
        toast('请选择所属部门。')
        return
      }

      const payload = buildUpdatePayload()
      if (!Object.keys(payload).length) {
        toast('未检测到需要保存的变更。')
        return
      }

      if (normalizeText(employeeForm.value.managerEmployeeNo) === selectedEmployee.value.employeeNo) {
        toast('直属上级不能设置为员工本人。')
        return
      }

      actionState.value = 'save'

      try {
        const employeeId = selectedEmployee.value.id
        const updated = await updateEmployee(employeeId, payload)
        selectedEmployee.value = updated
        await loadEmployees()

        let refreshed = updated
        try {
          refreshed = await fetchEmployeeDetail(employeeId)
        } catch {
          refreshed = updated
        }

        const fromList = employees.value.find((item) => item.id === employeeId)
        const merged = mergeEmployeeRecords(fromList, refreshed, employees.value)
        selectedEmployee.value = merged

        const listIndex = employees.value.findIndex((item) => item.id === employeeId)
        if (listIndex >= 0) {
          employees.value[listIndex] = {
            ...employees.value[listIndex],
            ...merged
          }
        }

        closeManagerPicker()
        closeDepartmentPicker()
        syncFormFromEmployee(selectedEmployee.value)
        employeeDetailSnapshot.value = captureEmployeeDetailSnapshot(employeeForm.value)
        toast('员工信息已保存并生效。')
      } catch (error) {
        toast(error?.message || '员工信息保存失败，请稍后重试。')
      } finally {
        actionState.value = ''
      }
    }

    function disableEmployeeAccount() {
      if (!selectedEmployee.value || disableActionDisabled.value) {
        return
      }

      disableDialogOpen.value = true
    }

    function closeDisableDialog() {
      if (actionState.value === 'disable') {
        return
      }

      disableDialogOpen.value = false
    }

    async function confirmDisableEmployeeAccount() {
      if (!selectedEmployee.value || disableActionDisabled.value) {
        return
      }

      const shouldEnable = selectedEmployeeDisabled.value
      const actionCopy = shouldEnable
        ? {
            successMessage: '员工账号已启用。',
            failureMessage: '启用账号失败，请稍后重试。'
          }
        : {
            successMessage: '员工账号已停用。',
            failureMessage: '停用账号失败，请稍后重试。'
          }

      actionState.value = 'disable'

      try {
        const updated = shouldEnable
          ? await enableEmployee(selectedEmployee.value.id)
          : await disableEmployee(selectedEmployee.value.id)
        disableDialogOpen.value = false
        selectedEmployee.value = updated
        await loadEmployees()
        toast(actionCopy.successMessage)
      } catch (error) {
        toast(error?.message || actionCopy.failureMessage)
      } finally {
        actionState.value = ''
      }
    }

    function openImportFilePicker() {
      importFileInput.value?.click()
    }

    function handleImportFileChange(event) {
      const file = event.target.files?.[0]
      event.target.value = ''

      if (!file) {
        return
      }

      pendingImportFile.value = file
      importConfirmDialogOpen.value = true
    }

    function closeImportConfirmDialog() {
      if (actionState.value === 'import') {
        return
      }

      importConfirmDialogOpen.value = false
      pendingImportFile.value = null
    }

    function closeImportErrorDialog() {
      importErrorDialogOpen.value = false
      importErrors.value = []
      importResultMessage.value = ''
    }

    async function handleDownloadTemplate() {
      try {
        await downloadEmployeeImportTemplate()
        toast('员工导入模板已开始下载。')
      } catch (error) {
        toast(error?.message || '模板下载失败，请稍后重试。')
      }
    }

    async function handleExportEmployees() {
      actionState.value = 'export'

      try {
        await exportEmployees({
          status: activeTab.value,
          keyword: searchKeyword.value.trim()
        })
        toast('员工目录已开始导出。')
      } catch (error) {
        toast(error?.message || '员工导出失败，请稍后重试。')
      } finally {
        actionState.value = ''
      }
    }

    async function confirmImportEmployees() {
      const file = pendingImportFile.value
      if (!file) {
        closeImportConfirmDialog()
        return
      }

      actionState.value = 'import'

      try {
        const result = await importEmployees(file)

        if (!result?.success) {
          importErrors.value = Array.isArray(result?.errors) ? result.errors : []
          importResultMessage.value =
            result?.message || '导入未执行，请根据下方错误提示修正 Excel 后重试。'
          importConfirmDialogOpen.value = false
          importErrorDialogOpen.value = true
          pendingImportFile.value = null
          return
        }

        importConfirmDialogOpen.value = false
        pendingImportFile.value = null
        await loadEmployees()
        toast(result.message || '员工导入成功。')
      } catch (error) {
        toast(error?.message || '员工导入失败，请稍后重试。')
      } finally {
        actionState.value = ''
      }
    }

    async function loadEmployees() {
      loading.value = true
      errorMessage.value = ''

      const [employeesResult, metaResult] = await Promise.allSettled([
        fetchEmployees(),
        fetchEmployeeMeta()
      ])

      if (employeesResult.status !== 'fulfilled') {
        employees.value = []
        roleOptions.value = [...FALLBACK_ROLE_OPTIONS]
        organizationUnitOptions.value = []
        selectedEmployee.value = null
        errorMessage.value =
          employeesResult.reason?.message || '员工数据加载失败，请稍后重试。'
        loading.value = false
        return
      }

      const roster = Array.isArray(employeesResult.value) ? employeesResult.value : []
      employees.value = roster.map((item) => enrichEmployeeRecord(item, roster))

      if (metaResult.status === 'fulfilled') {
        roleOptions.value = resolveRoleOptions(metaResult.value?.roleOptions, employees.value)
        organizationUnitOptions.value = resolveOrganizationOptions(
          metaResult.value?.organizationOptions
        )
      } else {
        roleOptions.value = resolveRoleOptions([], employees.value)
        organizationUnitOptions.value = []
      }

      if (!DEFAULT_STATUS_TABS.includes(activeTab.value)) {
        activeTab.value = DEFAULT_STATUS_TABS[0]
      }

      if (selectedEmployee.value) {
        const preserved = selectedEmployee.value
        const fromList = employees.value.find((item) => item.id === preserved.id) || null
        selectedEmployee.value = mergeEmployeeRecords(fromList, preserved, employees.value)
      }

      loading.value = false
    }

    onMounted(() => {
      document.addEventListener('click', handleDocumentClick)
      loadEmployees().catch((error) => {
        employees.value = []
        roleOptions.value = [...FALLBACK_ROLE_OPTIONS]
        organizationUnitOptions.value = []
        selectedEmployee.value = null
        errorMessage.value = error?.message || '员工数据加载失败，请稍后重试。'
        loading.value = false
      })
    })

    onBeforeUnmount(() => {
      document.removeEventListener('click', handleDocumentClick)
    })

    return {
      tabs,
      activeTab,
      employeeForm,
      roleCount,
      syncAgeFromBirthDate,
      syncBirthDateFromAge,
      selectedRoleLabels,
      selectedEmployeeDisabled,
      statusActionCopy,
      actionState,
      actionBusy,
      importExportBusy,
      importFileInput,
      importConfirmDialogOpen,
      importErrorDialogOpen,
      importErrors,
      importResultMessage,
      openImportFilePicker,
      handleImportFileChange,
      closeImportConfirmDialog,
      closeImportErrorDialog,
      handleDownloadTemplate,
      handleExportEmployees,
      confirmImportEmployees,
      disableActionDisabled,
      selectedEmployee,
      roleOptions,
      employees,
      visibleEmployees,
      employeeEmptyState,
      searchKeyword,
      selectedDepartment,
      selectedGrade,
      selectedRole,
      activeFilterPopover,
      currentPage,
      pageSize,
      pageSizes,
      pageSizeOptions,
      departmentOptions,
      gradeOptions,
      roleFilterOptions,
      managerPickerOpen,
      managerSearchKeyword,
      managerDisplayLabel,
      hasManagerAssignment,
      departmentPickerOpen,
      departmentSearchKeyword,
      departmentDisplayLabel,
      filteredDepartmentOptions,
      toggleDepartmentPicker,
      closeDepartmentPicker,
      selectDepartment,
      resolveDepartmentSelectionFromKeyword,
      recentEmployeeHistory,
      formatEmployeeHistoryTime,
      filteredManagerOptions,
      toggleManagerPicker,
      closeManagerPicker,
      selectManager,
      resolveManagerSelectionFromKeyword,
      activeFilterTokens,
      hasActiveFilters,
      hasEmployeeFilters,
      totalCount,
      totalPages,
      resetFilters,
      handleEmployeeEmptyAction,
      openEmployeeDetail,
      closeEmployeeDetail,
      closeDisableDialog,
      confirmDisableEmployeeAccount,
      saveEmployeeChanges,
      disableDialogOpen,
      disableEmployeeAccount,
      changePageSize,
      toggleFilterPopover,
      closeFilterPopover,
      selectFilter,
      loading,
      errorMessage,
      loadEmployees
    }
  }
}
