<script lang="ts" setup>
import type { FormInstance, FormRules } from 'element-plus'
import type {
  DeptOption,
  DetailItem,
  ExtPropertyItem,
  OperationMeeting,
  OperationMeetingDrawerMode,
  OperationMeetingForm,
  OperationMeetingFormItem
} from './types'
import MeetingDetailDrawer from './components/meeting-detail-drawer.vue'
import OperationMeetingContent from './components/operation-meeting-content.vue'
import { OperationMeetingApi } from '@/api/pms/meeting'
import { useWindowSize } from '@vueuse/core'

interface Props {
  visible: boolean
  id?: number
  type: 'create' | 'edit' | 'view'
  mode?: OperationMeetingDrawerMode
  meetingSeries?: string
  year?: string | number
  deptOptions?: DeptOption[]
}

const props = withDefaults(defineProps<Props>(), {
  type: 'create',
  mode: 'fill',
  meetingSeries: '',
  year: '',
  deptOptions: () => []
})

const emits = defineEmits(['update:visible', 'success'])
const message = useMessage()

const operationMeetingRef = ref<FormInstance>()
const loading = ref(false)
const { width } = useWindowSize()

const extProperties = ref<ExtPropertyItem[]>([])
const extPropertiesLoaded = ref(false)
const form = ref<OperationMeetingFormItem[]>([createMeetingFormItem()])
const detailDrawerVisible = ref(false)
const detailEditingIndex = ref(-1)
const detailFormType = ref<'create' | 'edit'>('create')
const detailForm = ref<DetailItem>(createDetailItem())
const filterForm = reactive({
  companyFilterValue: ''
})

const isMobile = computed(() => width.value <= 768)
const drawerSize = computed(() => (isMobile.value ? '100%' : '100%'))
const isSummaryMode = computed(() => props.mode === 'summary')
const activeFormItem = computed(() => form.value[0] || createMeetingFormItem())
const operationMeeting = computed(() => activeFormItem.value.meeting)
const detailItems = computed(() => activeFormItem.value.details)
const detailDrawerType = computed(() => (isSummaryMode.value ? 'view' : props.type))

function createDetailItem(): DetailItem {
  return {
    raw: {},
    projectName: '',
    currentRevenue: undefined,
    cumulativeRevenue: undefined,
    beforeRevenue: undefined,
    currentOnAccount: undefined,
    cumulativeOnAccount: undefined,
    beforeOnAccount: undefined,
    currentPayment: undefined,
    cumulativePayment: undefined,
    beforePayment: undefined,
    plannedWorkload: '',
    actualCompletion: '',
    equipmentUtilizationRate: undefined,
    keyWorkCompletion: '',
    problemsAnalysis: '',
    qhse: '',
    nextPlannedWorkload: '',
    priorityTasks: '',
    extProperty: cloneExtProperties(extProperties.value)
  }
}

function createMeetingFormItem(
  meeting: OperationMeetingForm = {},
  details: DetailItem[] = [],
  extProperty: ExtPropertyItem[] = [],
  index = 0
): OperationMeetingFormItem {
  const id = meeting.id === undefined || meeting.id === null ? 'new' : String(meeting.id)

  return {
    key: `${id}-${index}`,
    meeting,
    details,
    extProperty,
    beforeRevenue: undefined,
    beforeOnAccount: undefined,
    beforePayment: undefined
  }
}

const cloneDetailItem = (data?: Partial<DetailItem>): DetailItem => ({
  raw: data?.raw || {},
  projectName: data?.projectName || '',
  currentRevenue: data?.currentRevenue,
  cumulativeRevenue: data?.cumulativeRevenue,
  beforeRevenue: data?.beforeRevenue,
  currentOnAccount: data?.currentOnAccount,
  cumulativeOnAccount: data?.cumulativeOnAccount,
  beforeOnAccount: data?.beforeOnAccount,
  currentPayment: data?.currentPayment,
  cumulativePayment: data?.cumulativePayment,
  beforePayment: data?.beforePayment,
  plannedWorkload: data?.plannedWorkload || '',
  actualCompletion: data?.actualCompletion || '',
  equipmentUtilizationRate: data?.equipmentUtilizationRate,
  keyWorkCompletion: data?.keyWorkCompletion || '',
  problemsAnalysis: data?.problemsAnalysis || '',
  qhse: data?.qhse || '',
  nextPlannedWorkload: data?.nextPlannedWorkload || '',
  priorityTasks: data?.priorityTasks || '',
  extProperty: cloneExtProperties(data?.extProperty)
})

const parseNumberValue = (value: unknown) => {
  if (value === undefined || value === null || value === '') return undefined
  const parsed = Number(String(value).replace('%', ''))

  return Number.isNaN(parsed) ? undefined : parsed
}

const parseMeetingSeries = (value: unknown) => {
  if (value === undefined || value === null || value === '') return undefined
  const matched = String(value).match(/\d+/)

  return matched ? Number(matched[0]) : undefined
}

const normalizeTextValue = (value: unknown) => String(value ?? '').replace(/\\r\\n|\\n/g, '\n')

function normalizeExtProperty(data?: Partial<ExtPropertyItem>): ExtPropertyItem {
  const dataType = normalizeTextValue(data?.dataType)

  return {
    name: normalizeTextValue(data?.name),
    identifier: normalizeTextValue(data?.identifier),
    dataType,
    required: data?.required ?? 0,
    unit: normalizeTextValue(data?.unit),
    accessMode: normalizeTextValue(data?.accessMode),
    defaultValue: normalizeTextValue(data?.defaultValue),
    maxValue: data?.maxValue,
    minValue: data?.minValue,
    sort: parseNumberValue(data?.sort),
    actualValue: dataType === 'double' ? parseNumberValue(data?.actualValue) : data?.actualValue,
    dropdownList: data?.dropdownList
  }
}

function parseExtPropertyList(data?: ExtPropertyItem[] | unknown): unknown[] {
  if (Array.isArray(data)) return data

  if (typeof data === 'string') {
    const text = data.trim()
    if (!text) return []

    try {
      return parseExtPropertyList(JSON.parse(text))
    } catch {
      return []
    }
  }

  if (data && typeof data === 'object') {
    const values = Object.values(data as Record<string, unknown>)

    return values.every((item) => item && typeof item === 'object') ? values : []
  }

  return []
}

function cloneExtProperties(data?: ExtPropertyItem[] | unknown): ExtPropertyItem[] {
  return parseExtPropertyList(data)
    .map((item) => normalizeExtProperty(item as Partial<ExtPropertyItem>))
    .filter((item) => item.identifier)
}

function mergeExtProperties(
  defaults: ExtPropertyItem[],
  current?: ExtPropertyItem[] | unknown
): ExtPropertyItem[] {
  const currentItems = cloneExtProperties(current)
  if (!defaults.length) return currentItems

  const currentMap = new Map(currentItems.map((item) => [item.identifier, item]))
  const defaultIdentifiers = new Set(defaults.map((item) => item.identifier))
  const mergedDefaults = defaults.map((item) => {
    const currentItem = currentMap.get(item.identifier)

    return {
      ...normalizeExtProperty(item),
      actualValue:
        currentItem && 'actualValue' in currentItem ? currentItem.actualValue : item.actualValue
    }
  })
  const extraItems = currentItems.filter((item) => !defaultIdentifiers.has(item.identifier))

  return [...mergedDefaults, ...extraItems].sort(
    (a, b) => Number(a.sort || 0) - Number(b.sort || 0)
  )
}

const normalizeDetailItem = (data?: Record<string, unknown>): DetailItem => ({
  raw: data || {},
  projectName: normalizeTextValue(data?.projectName),
  currentRevenue: parseNumberValue(data?.currentRevenue),
  cumulativeRevenue: parseNumberValue(data?.cumulativeRevenue),
  beforeRevenue: parseNumberValue(data?.beforeRevenue),
  currentOnAccount: parseNumberValue(data?.currentOnAccount),
  cumulativeOnAccount: parseNumberValue(data?.cumulativeOnAccount),
  beforeOnAccount: parseNumberValue(data?.beforeOnAccount),
  currentPayment: parseNumberValue(data?.currentPayment),
  cumulativePayment: parseNumberValue(data?.cumulativePayment),
  beforePayment: parseNumberValue(data?.beforePayment),
  plannedWorkload: normalizeTextValue(data?.plannedWorkload),
  actualCompletion: normalizeTextValue(data?.actualCompletion),
  equipmentUtilizationRate: parseNumberValue(data?.equipmentUtilizationRate),
  keyWorkCompletion: normalizeTextValue(data?.keyWorkCompletion),
  problemsAnalysis: normalizeTextValue(data?.problemsAnalysis),
  qhse: normalizeTextValue(data?.qhse),
  nextPlannedWorkload: normalizeTextValue(data?.nextPlannedWorkload),
  priorityTasks: normalizeTextValue(data?.priorityTasks),
  extProperty: mergeExtProperties(extProperties.value, data?.extProperty)
})

const normalizeMeetingFormItem = (
  data: Record<string, unknown> = {},
  index: number
): OperationMeetingFormItem => {
  const details = Array.isArray(data.details) ? data.details : []
  const id = data.id === undefined || data.id === null ? index : String(data.id)

  return {
    key: `${id}-${index}`,
    meeting: {
      id: parseNumberValue(data.id),
      deptId: parseNumberValue(data.deptId),
      companyName: normalizeTextValue(data.companyName),
      meetingDate: data.meetingDate as number | string | Date | undefined,
      support: normalizeTextValue(data.support),
      cumulative: data.cumulative as boolean | undefined,
      meetingSeries: parseMeetingSeries(data.meetingSeries)
    },
    details: details.map((item) => normalizeDetailItem(item as Record<string, unknown>)),
    extProperty: cloneExtProperties(data.extProperty),
    beforeRevenue: parseNumberValue(data.beforeRevenue),
    beforeOnAccount: parseNumberValue(data.beforeOnAccount),
    beforePayment: parseNumberValue(data.beforePayment)
  }
}

const loadCurrentCompanyExtProperties = async () => {
  if (extPropertiesLoaded.value) return

  try {
    const data = await OperationMeetingApi.getCurrentCompanyExtProperties()
    extProperties.value = cloneExtProperties(data)
  } catch {
    extProperties.value = []
  } finally {
    extPropertiesLoaded.value = true
  }
}

const requiredTextRule = (message: string) => [
  {
    required: true,
    whitespace: true,
    message,
    trigger: ['blur', 'change']
  }
]

const operationMeetingRules = reactive<FormRules>({
  meetingDate: [{ required: true, message: '请选择会议日期', trigger: 'change' }],
  meetingSeries: [
    {
      required: true,
      type: 'number',
      message: '请输入会议期次',
      trigger: ['blur', 'change']
    },
    {
      type: 'number',
      min: 1,
      message: '会议期次最小为1',
      trigger: ['blur', 'change']
    }
  ],
  support: requiredTextRule('请输入其他重点事项及需要集团协调事项')
})

const handleAddDetailItem = () => {
  if (isSummaryMode.value) return

  detailFormType.value = 'create'
  detailEditingIndex.value = -1
  detailForm.value = createDetailItem()
  detailDrawerVisible.value = true
}

const handleEditDetailItem = (row: DetailItem, index: number) => {
  detailFormType.value = 'edit'
  detailEditingIndex.value = isSummaryMode.value ? -1 : index
  detailForm.value = cloneDetailItem(row)
  detailDrawerVisible.value = true
}

const handleDeleteDetailItem = (index: number) => {
  if (isSummaryMode.value) return

  activeFormItem.value.details.splice(index, 1)
}

const handleDetailDrawerChange = (visible: boolean) => {
  detailDrawerVisible.value = visible

  if (!visible) {
    detailForm.value = createDetailItem()
    detailFormType.value = 'create'
    detailEditingIndex.value = -1
  }
}

const updateOperationMeeting = (nextOperationMeeting: OperationMeetingForm) => {
  activeFormItem.value.meeting = nextOperationMeeting
}

const saveDetailItem = (item: DetailItem) => {
  if (isSummaryMode.value) return

  const nextItem = cloneDetailItem(item)
  const currentDetails = activeFormItem.value.details

  if (detailEditingIndex.value > -1) {
    currentDetails.splice(detailEditingIndex.value, 1, nextItem)
  } else {
    currentDetails.push(nextItem)
  }

  handleDetailDrawerChange(false)
}

const resetForm = () => {
  form.value = isSummaryMode.value ? [] : [createMeetingFormItem()]
  filterForm.companyFilterValue = ''
  loading.value = false
  handleDetailDrawerChange(false)
  nextTick(() => operationMeetingRef.value?.clearValidate())
}

const loadOperationMeetingDetail = async (id: number) => {
  await loadCurrentCompanyExtProperties()

  loading.value = true
  try {
    const data = await OperationMeetingApi.getOperationMeeting(id)

    if (!props.visible) return

    form.value = [normalizeMeetingFormItem((data || {}) as Record<string, unknown>, 0)]
    nextTick(() => operationMeetingRef.value?.clearValidate())
  } finally {
    loading.value = false
  }
}

const loadSummaryDetails = async () => {
  await loadCurrentCompanyExtProperties()

  if (!props.meetingSeries || !props.year) {
    form.value = []
    return
  }

  const meetingSeries = props.meetingSeries
  const year = props.year
  loading.value = true
  try {
    const res = await OperationMeetingApi.getSummarizedProjectDetails({
      meetingSeries,
      year
    })

    if (
      !props.visible ||
      props.mode !== 'summary' ||
      props.meetingSeries !== meetingSeries ||
      props.year !== year
    ) {
      return
    }

    const meetings = Array.isArray(res) ? res : []
    form.value = meetings.map((item, index) =>
      normalizeMeetingFormItem(item as Record<string, unknown>, index)
    )
    filterForm.companyFilterValue = ''
  } finally {
    loading.value = false
  }
}

const handleVisibleChange = (visible: boolean) => {
  emits('update:visible', visible)

  if (!visible) {
    resetForm()
  }
}

watch(
  () => [props.visible, props.id, props.type, props.mode, props.meetingSeries, props.year] as const,
  async ([visible, id, type, mode]) => {
    if (!visible) return

    await loadCurrentCompanyExtProperties()
    if (!props.visible) return

    if (mode === 'summary') {
      loadSummaryDetails()
      return
    }

    if (type === 'create') {
      resetForm()
      return
    }

    if (id) {
      loadOperationMeetingDetail(id)
      return
    }

    resetForm()
  }
)

const formatMeetingSeries = (value: number) => `第${value}期`

const getMeetingDateTimestamp = (value: OperationMeetingForm['meetingDate']) => {
  if (value instanceof Date) return value.getTime()
  if (typeof value === 'number') return value
  if (typeof value === 'string') {
    const timestamp = new Date(value).getTime()

    return Number.isNaN(timestamp) ? undefined : timestamp
  }

  return undefined
}

const buildOperationMeetingPayload = (): Partial<OperationMeeting> => {
  const payload: Partial<OperationMeeting> = {
    meetingDate: getMeetingDateTimestamp(operationMeeting.value.meetingDate),
    meetingSeries: formatMeetingSeries(operationMeeting.value.meetingSeries as number),
    support: operationMeeting.value.support || ''
  }

  if (operationMeeting.value.id) {
    payload.id = operationMeeting.value.id
  }

  return payload
}

const buildDetailPayload = (item: DetailItem): Omit<DetailItem, 'raw'> => {
  const { raw, ...rest } = item
  return rest
}

const submitForm = async () => {
  if (isSummaryMode.value || props.type === 'view' || !operationMeetingRef.value) return

  const valid = await operationMeetingRef.value.validate().catch(() => false)

  if (!valid) return

  if (detailItems.value.length === 0) {
    message.warning('请先新增至少一条会议明细')
    return
  }

  loading.value = true
  try {
    await OperationMeetingApi.saveBatch({
      operationMeeting: buildOperationMeetingPayload(),
      details: detailItems.value.map((item) => buildDetailPayload(item))
    })

    message.success('保存成功')
    handleVisibleChange(false)
    emits('success')
  } finally {
    loading.value = false
  }
}
</script>

<template>
  <el-drawer
    :model-value="visible"
    @update:model-value="handleVisibleChange"
    body-class="bg-gray-100"
    footer-class="p-4!"
    :size="drawerSize"
    :with-header="false">
    <el-form
      ref="operationMeetingRef"
      label-width="auto"
      label-position="top"
      size="default"
      :model="isSummaryMode ? filterForm : operationMeeting"
      :rules="isSummaryMode ? undefined : operationMeetingRules"
      v-loading="loading"
      scroll-to-error
      require-asterisk-position="right">
      <OperationMeetingContent
        v-if="!isSummaryMode || form.length"
        :meeting="operationMeeting"
        :details="detailItems"
        :type="detailDrawerType"
        :mode="mode"
        :meetings="form"
        :ext-properties="extProperties"
        :dept-options="deptOptions"
        :loading="loading"
        v-model:company-filter-value="filterForm.companyFilterValue"
        @update:meeting="updateOperationMeeting"
        @add-detail="handleAddDetailItem"
        @edit-detail="handleEditDetailItem"
        @delete-detail="handleDeleteDetailItem" />
      <el-empty v-else description="暂无汇总会议详情" :image-size="100" />
    </el-form>

    <template #footer>
      <el-button size="default" @click="handleVisibleChange(false)">
        {{ isSummaryMode ? '关闭' : '取消' }}
      </el-button>
      <el-button
        v-if="!isSummaryMode && type !== 'view'"
        size="default"
        type="primary"
        :loading="loading"
        @click="submitForm">
        保存
      </el-button>
    </template>

    <MeetingDetailDrawer
      :visible="detailDrawerVisible"
      :detail="detailForm"
      :type="detailDrawerType"
      :form-type="detailFormType"
      :ext-properties="extProperties"
      @update:visible="handleDetailDrawerChange"
      @save="saveDetailItem" />
  </el-drawer>
</template>
