<script setup lang="ts">
import { computed, defineComponent, h, nextTick, onActivated, onBeforeUnmount, onMounted, ref, shallowRef, watch, type PropType } from 'vue'
import { useI18n } from 'vue-i18n'
import { AgGridVue } from 'ag-grid-vue3'
import type { ColDef, FirstDataRenderedEvent, GridApi, GridOptions, GridReadyEvent, ICellRendererParams, RowDataUpdatedEvent } from 'ag-grid-community'
import { AG_GRID_LOCALE_CN } from '@ag-grid-community/locale'
import { myTheme, gridOptions } from '@/lib/diyAgGridOptions'
import { withReadonlyAutoHeight } from '@/lib/agGridReadonlyAutoHeight'
import { formatThousands, formatThousandsFlexible } from '@/lib/numberFormat'
import { roundTo, toFiniteNumberOrNull } from '@/lib/decimal'
import { useZxFwPricingStore } from '@/pinia/zxFwPricing'
import { additionalWorkList, reserveList } from '@/sql'

type SummaryRowType = 'service' | 'additional' | 'reserve' | 'total'

interface SummaryRow {
  id: string
  rowType: SummaryRowType
  code: string | { richText?: Array<{ text?: string; font?: { italic?: boolean; vertAlign?: string } }> }
  name: string
  investScale: number | null
  landScale: number | null
  workload: number | null
  hourly: number | null
  subtotal: number | null
  finalFee: number | null
}

interface RateMethodStateLike {
  rate?: unknown
  budgetFee?: unknown
}
interface HourlyMethodRowLike {
  serviceBudget?: unknown
  adoptedBudgetUnitPrice?: unknown
  personnelCount?: unknown
  workdayCount?: unknown
}
interface HourlyMethodStateLike {
  detailRows?: HourlyMethodRowLike[]
}
interface QuantityMethodRowLike {
  id?: unknown
  budgetFee?: unknown
  quantity?: unknown
  unitPrice?: unknown
}
interface QuantityMethodStateLike {
  detailRows?: QuantityMethodRowLike[]
}

const props = defineProps<{
  contractId: string
}>()
const { t } = useI18n()

const zxFwPricingStore = useZxFwPricingStore()
const gridApi = shallowRef<GridApi<SummaryRow> | null>(null)
const rowData = ref<SummaryRow[]>([])
const explanationText = ref('')
let reloadTimer: ReturnType<typeof setTimeout> | null = null

/**
 * 对数值数组求和（保留3位小数）
 * @param values 数值数组（可包含 null/undefined）
 * @returns 求和结果（无有效值时返回 null）
 */
const sum3 = (values: Array<number | null | undefined>) => {
  const valid = values.filter((v): v is number => toFiniteNumberOrNull(v) != null)
  if (valid.length === 0) return null
  return roundTo(valid.reduce((a, b) => a + b, 0), 3)
}

/**
 * 计算时工法的总费用
 * 遍历时工法明细行，优先使用 serviceBudget，否则通过 adoptedBudgetUnitPrice × personnelCount × workdayCount 计算
 * @param state 时工法状态对象
 * @returns 总费用（保留3位小数，无有效值时返回 null）
 */
const sumHourlyMethodFee = (state: HourlyMethodStateLike | null): number | null => {
  const rows = Array.isArray(state?.detailRows) ? state.detailRows : []
  if (rows.length === 0) return null
  let total = 0
  let hasValid = false
  for (const row of rows) {
    const rowBudget = toFiniteNumberOrNull(row?.serviceBudget)
    if (rowBudget != null) {
      total += rowBudget
      hasValid = true
      continue
    }
    const adopted = toFiniteNumberOrNull(row?.adoptedBudgetUnitPrice)
    const personnel = toFiniteNumberOrNull(row?.personnelCount)
    const workday = toFiniteNumberOrNull(row?.workdayCount)
    if (adopted == null || personnel == null || workday == null) continue
    total += adopted * personnel * workday
    hasValid = true
  }
  return hasValid ? roundTo(total, 3) : null
}

/**
 * 计算工程量清单法的总费用
 * 遍历工程量清单明细行，优先使用 budgetFee，否则通过 quantity × unitPrice 计算
 * 跳过固定小计行（fee-subtotal-fixed）
 * @param state 工程量清单法状态对象
 * @returns 总费用（保留3位小数，无有效值时返回 null）
 */
const sumQuantityMethodFee = (state: QuantityMethodStateLike | null): number | null => {
  const rows = Array.isArray(state?.detailRows) ? state.detailRows : []
  if (rows.length === 0) return null
  let total = 0
  let hasValid = false
  for (const row of rows) {
    if (String(row?.id || '') === 'fee-subtotal-fixed') continue
    const budget = toFiniteNumberOrNull(row?.budgetFee)
    if (budget != null) {
      total += budget
      hasValid = true
      continue
    }
    const quantity = toFiniteNumberOrNull(row?.quantity)
    const unitPrice = toFiniteNumberOrNull(row?.unitPrice)
    if (quantity == null || unitPrice == null) continue
    total += quantity * unitPrice
    hasValid = true
  }
  return hasValid ? roundTo(total, 3) : null
}

/**
 * 加载合同段某行的三种计费方式（费率法、时工法、工程量清单法）并汇总
 * @param mainStorageKey 主存储键（如 htExtraFee-{contractId}-additional-work）
 * @param rowId 行ID
 * @returns 包含subtotal（小计）、m0（费率法详情）、m4（时工法详情）、m5（工程量清单法详情）的对象
 */
const loadHtMethodSummaryByRow = async (mainStorageKey: string, rowId: string): Promise<{
  subtotal: number | null
  m0: { coe: string; fee: number } | null
  m4: { fee: number } | null
  m5: { fee: number } | null
}> => {
  const [rateState, hourlyState, quantityState] = await Promise.all([
    zxFwPricingStore.loadHtFeeMethodState<RateMethodStateLike>(mainStorageKey, rowId, 'rate-fee'),
    zxFwPricingStore.loadHtFeeMethodState<HourlyMethodStateLike>(mainStorageKey, rowId, 'hourly-fee'),
    zxFwPricingStore.loadHtFeeMethodState<QuantityMethodStateLike>(mainStorageKey, rowId, 'quantity-unit-price-fee')
  ])
  const rateFee = toFiniteNumberOrNull(rateState?.budgetFee)
  const rateValue = toFiniteNumberOrNull(rateState?.rate)
  const hourlyFee = sumHourlyMethodFee(hourlyState)
  const quantityFee = sumQuantityMethodFee(quantityState)
  const subtotal = sum3([rateFee, hourlyFee, quantityFee])
  return {
    subtotal,
    m0: rateFee != null
      ? {
        coe: rateValue == null ? '--' : String(rateValue),
        fee: roundTo(rateFee, 2)
      }
      : null,
    m4: hourlyFee != null ? { fee: roundTo(hourlyFee, 2) } : null,
    m5: quantityFee != null ? { fee: roundTo(quantityFee, 2) } : null
  }
}

/**
 * 构建附加工作或预备费的汇总行数据
 * @param rowType 行类型（'additional' 附加工作 或 'reserve' 预备费）
 * @param list 费用项目列表（包含 id、name、code）
 * @returns 包含 rows（汇总行数组）和 explainLines（说明文本数组）的对象
 */
const buildFeeRows = async (
  rowType: 'additional' | 'reserve',
  list: Array<{ id: string | number; name: string; code: unknown }>
): Promise<{ rows: SummaryRow[]; explainLines: string[] }> => {
  const mainStorageKey = `htExtraFee-${props.contractId}-${rowType === 'additional' ? 'additional-work' : 'reserve'}`
  await zxFwPricingStore.loadHtFeeMainState(mainStorageKey)
  const tuples = await Promise.all(
    list.map(async item => {
      const summary = await loadHtMethodSummaryByRow(mainStorageKey, String(item.id))
      const lineParts: string[] = []
      if (summary.m0) {
        lineParts.push(t('htSummary.explainByRate', { rate: summary.m0.coe, fee: summary.m0.fee }))
      }
      if (summary.m4) {
        lineParts.push(t('htSummary.explainByHourly', { fee: summary.m4.fee }))
      }
      if (summary.m5) {
        lineParts.push(t('htSummary.explainByQuantity', { fee: summary.m5.fee }))
      }
      const linePrefix = rowType === 'additional' ? t('htSummary.additionalPrefix') : t('htSummary.reservePrefix')
      const explainLine = lineParts.length > 0 ? `${linePrefix}-${item.name}：${lineParts.join(';')}` : ''
      const row: SummaryRow = {
        id: `${rowType}-${item.id}`,
        rowType,
        code: item.code as SummaryRow['code'],
        name: item.name,
        investScale: null,
        landScale: null,
        workload: null,
        hourly: null,
        subtotal: summary.subtotal,
        finalFee: summary.subtotal
      }
      return { row, explainLine }
    })
  )
  const rows = tuples.map(item => item.row).filter(row => row.subtotal != null)
  const explainLines = tuples
    .filter(item => item.row.subtotal != null && item.explainLine)
    .map(item => item.explainLine)
  return { rows, explainLines }
}

/**
 * 构建咨询服务汇总行数据
 * 从合同状态中筛选已选中的服务项，转换为 SummaryRow 格式
 * @returns 咨询服务汇总行数组
 */
const buildServiceRows = (): SummaryRow[] => {
  const contractState = zxFwPricingStore.getContractState(props.contractId)
  const selectedSet = new Set((contractState?.selectedIds || []).map(id => String(id)))
  const rows = Array.isArray(contractState?.detailRows) ? contractState!.detailRows : []
  return rows
    .filter(row => String(row.id) !== 'fixed-budget-c' && selectedSet.has(String(row.id)))
    .map(row => ({
      id: `service-${row.id}`,
      rowType: 'service' as const,
      code: row.code || '',
      name: row.name || '',
      investScale: toFiniteNumberOrNull(row.investScale),
      landScale: toFiniteNumberOrNull(row.landScale),
      workload: toFiniteNumberOrNull(row.workload),
      hourly: toFiniteNumberOrNull(row.hourly),
      subtotal: toFiniteNumberOrNull(row.subtotal),
      finalFee: toFiniteNumberOrNull((row as { finalFee?: unknown }).finalFee) ?? toFiniteNumberOrNull(row.subtotal)
    }))
}

/**
 * 重新加载所有汇总行数据
 * 包括：咨询服务、附加工作、预备费
 * 同时生成说明文本
 */
const reloadRows = async () => {
  await zxFwPricingStore.loadContract(props.contractId)
  const [additionalResult, reserveResult] = await Promise.all([
    buildFeeRows(
      'additional',
      additionalWorkList.map(item => ({ id: item.id, name: item.name, code: item.code }))
    ),
    buildFeeRows(
      'reserve',
      reserveList.map(item => ({ id: item.id, name: item.name, code: item.code }))
    )
  ])
  rowData.value = [...buildServiceRows(), ...additionalResult.rows, ...reserveResult.rows]
  const lines = [...additionalResult.explainLines, ...reserveResult.explainLines]
  explanationText.value = lines.join('\n')
}

/**
 * 延迟重新加载汇总数据（防抖 80ms）
 * 避免频繁触发数据加载
 */
const scheduleReload = () => {
  if (reloadTimer) clearTimeout(reloadTimer)
  reloadTimer = setTimeout(() => {
    void reloadRows()
  }, 80)
}

/**
 * 刷新签名（用于 watch 监听数据变化）
 * 监听合同数据、附加工作、预备费的主状态和方法状态变化
 */
const refreshSignature = computed(() => {
  const additionalKey = `htExtraFee-${props.contractId}-additional-work`
  const reserveKey = `htExtraFee-${props.contractId}-reserve`
  return JSON.stringify({
    contract: zxFwPricingStore.contracts[props.contractId] || null,
    addMain: zxFwPricingStore.htFeeMainStates[additionalKey] || null,
    reserveMain: zxFwPricingStore.htFeeMainStates[reserveKey] || null,
    addMethods: zxFwPricingStore.htFeeMethodStates[additionalKey] || null,
    reserveMethods: zxFwPricingStore.htFeeMethodStates[reserveKey] || null
  })
})

/**
 * 计算总计行数据
 * 对所有汇总行的各字段进行求和
 */
const totalRow = computed<SummaryRow>(() => {
  const sumField = (pick: (row: SummaryRow) => number | null | undefined) =>
    sum3(rowData.value.map(pick))
  return {
    id: 'summary-total-row',
    rowType: 'total',
    code: '',
    name: t('htSummary.total'),
    investScale: sumField(row => row.investScale),
    landScale: sumField(row => row.landScale),
    workload: sumField(row => row.workload),
    hourly: sumField(row => row.hourly),
    subtotal: null,
    finalFee: sumField(row => row.finalFee)
  }
})

const RichCodeRenderer = defineComponent({
  name: 'RichCodeRenderer',
  props: {
    params: {
      type: Object as PropType<ICellRendererParams<SummaryRow>>,
      required: true
    }
  },
  setup(props) {
    return () => {
      if (props.params.data?.rowType === 'total') {
        return h('span', props.params.data.name || t('htSummary.total'))
      }
      const value = props.params.value as SummaryRow['code']
      if (!value || typeof value === 'string') {
        return h('span', value || '')
      }
      const runs = Array.isArray(value.richText) ? value.richText : []
      return h(
        'span',
        { class: 'inline-flex items-baseline gap-[1px]' },
        runs.map((run, idx) =>
          h(
            'span',
            {
              key: `${idx}-${run.text || ''}`,
              style: {
                fontStyle: run?.font?.italic ? 'italic' : 'normal',
                verticalAlign: run?.font?.vertAlign === 'subscript' ? 'sub' : run?.font?.vertAlign === 'superscript' ? 'super' : 'baseline',
                fontSize: run?.font?.vertAlign ? '0.85em' : '1em'
              }
            },
            run?.text || ''
          )
        )
      )
    }
  }
})

const columnDefs: ColDef<SummaryRow>[] = [
  {
    headerName: t('htSummary.columns.code'),
    field: 'code',
    minWidth: 90,
    maxWidth: 140,
    colSpan: params => (params.data?.rowType === 'total' ? 2 : 1),
    valueFormatter: params => {
      if (params.data?.rowType === 'total') return params.data.name || t('htSummary.total')
      return typeof params.value === 'string' ? params.value : ''
    },
    cellRenderer: RichCodeRenderer
  },
  {
    headerName: t('htSummary.columns.name'),
    field: 'name',
    minWidth: 220,
    flex: 2,
    wrapText: true,
    autoHeight: true,
    cellStyle: { 'line-height': 1.6 }
  },
  {
    headerName: t('htSummary.columns.investScale'),
    field: 'investScale',
    minWidth: 120,
    flex: 1.2,
    headerClass: 'ag-right-aligned-header',
    cellClassRules: {
      'ag-right-aligned-cell': () => true
    },
    colSpan: params => {
      if (!params.data) return 1
      if (params.data.rowType === 'total') return 5
      if (params.data.rowType === 'additional' || params.data.rowType === 'reserve') return 5
      return 1
    },
    valueFormatter: params => {
      if (params.data?.rowType === 'total') return ''
      return params.value == null ? '' : formatThousandsFlexible(params.value, 3)
    }
  },
  {
    headerName: t('htSummary.columns.landScale'),
    field: 'landScale',
    minWidth: 120,
    flex: 1.2,
    headerClass: 'ag-right-aligned-header',
    cellClassRules: {
      'ag-right-aligned-cell': () => true
    },
    valueFormatter: params => {
      if (params.data?.rowType === 'total') return ''
      return params.value == null ? '' : formatThousandsFlexible(params.value, 3)
    }
  },
  {
    headerName: t('htSummary.columns.workload'),
    field: 'workload',
    minWidth: 110,
    flex: 1.2,
    headerClass: 'ag-right-aligned-header',
    cellClassRules: {
      'ag-right-aligned-cell': () => true
    },
    valueFormatter: params => {
      if (params.data?.rowType === 'total') return ''
      return params.value == null ? '' : formatThousandsFlexible(params.value, 3)
    }
  },
  {
    headerName: t('htSummary.columns.hourly'),
    field: 'hourly',
    minWidth: 110,
    flex: 1.2,
    headerClass: 'ag-right-aligned-header',
    cellClassRules: {
      'ag-right-aligned-cell': () => true
    },
    valueFormatter: params => {
      if (params.data?.rowType === 'total') return ''
      return params.value == null ? '' : formatThousandsFlexible(params.value, 3)
    }
  },
  {
    headerName: t('htSummary.columns.subtotal'),
    field: 'subtotal',
    minWidth: 120,
    flex: 1.2,
    headerClass: 'ag-right-aligned-header',
    cellClassRules: {
      'ag-right-aligned-cell': () => true
    },
    valueFormatter: params => (params.value == null ? '' : formatThousands(params.value, 2))
  },
  {
    headerName: t('htSummary.columns.finalFee'),
    field: 'finalFee',
    minWidth: 120,
    flex: 1.2,
    headerClass: 'ag-right-aligned-header',
    cellClassRules: {
      'ag-right-aligned-cell': () => true
    },
    valueFormatter: params => (params.value == null ? '' : formatThousands(params.value, 2))
  }
]
const gridColumnDefs = computed(() => withReadonlyAutoHeight(columnDefs))

const summaryGridOptions: GridOptions<SummaryRow> = {
  ...gridOptions,
  treeData: false,
  getDataPath: undefined,
  domLayout: 'autoHeight',
  suppressNoRowsOverlay: true,
  rowSelection: {
    mode: 'singleRow',
    checkboxes: false,
    enableClickSelection: false
  },
  getRowId: params => params.data.id,
  getRowClass: params => (params.data?.rowType === 'additional' || params.data?.rowType === 'reserve' ? 'ht-summary-fee-row' : '')
}

/**
 * AG Grid 准备就绪回调
 * 初始化 gridApi 并同步自动行高
 * @param event Grid 准备就绪事件
 */
const onGridReady = (event: GridReadyEvent<SummaryRow>) => {
  gridApi.value = event.api
  void syncAutoRowHeights()
}

/**
 * 检查 Grid API 是否可用
 * @param api Grid API 实例
 * @returns API 是否有效
 */
const isGridApiAlive = (api: GridApi<SummaryRow> | null | undefined): api is GridApi<SummaryRow> =>
  Boolean(api && !api.isDestroyed?.())

/**
 * 同步自动行高
 * 触发 AG Grid 重新计算行高并刷新单元格
 */
const syncAutoRowHeights = async () => {
  await nextTick()
  const api = gridApi.value
  if (!isGridApiAlive(api)) return
  api.onRowHeightChanged()
  api.refreshCells({ force: true })
}

/**
 * 首次数据渲染完成回调
 * 同步自动行高以确保显示正确
 * @param _event 首次数据渲染事件
 */
const onFirstDataRendered = (_event: FirstDataRenderedEvent<SummaryRow>) => {
  void syncAutoRowHeights()
}

/**
 * 行数据更新回调
 * 同步自动行高以适应新数据
 * @param _event 行数据更新事件
 */
const onRowDataUpdated = (_event: RowDataUpdatedEvent<SummaryRow>) => {
  void syncAutoRowHeights()
}

watch(refreshSignature, (next, prev) => {
  if (next === prev) return
  scheduleReload()
})

watch(
  () => rowData.value.length,
  () => {
    void syncAutoRowHeights()
  }
)

onMounted(() => {
  void reloadRows()
})

onActivated(() => {
  void reloadRows()
})

onBeforeUnmount(() => {
  if (isGridApiAlive(gridApi.value)) {
    gridApi.value.stopEditing()
  }
  gridApi.value = null
})
</script>

<template>
  <div class="flex flex-col gap-3">
    <div class="rounded-lg border bg-card xmMx flex flex-col overflow-hidden">
      <div class="flex items-center justify-between border-b px-3 py-2">
        <h3 class="text-xs font-semibold text-foreground leading-none">
          {{ t('htSummary.title') }}
        </h3>
      </div>
      <div class="ag-theme-quartz w-full">
        <AgGridVue
          :style="{ width: '100%' }"
          :rowData="rowData"
          :pinnedBottomRowData="[totalRow]"
          :columnDefs="gridColumnDefs"
          :gridOptions="summaryGridOptions"
          :theme="myTheme"
          :animateRows="true"
          :localeText="AG_GRID_LOCALE_CN"
          @grid-ready="onGridReady"
          @first-data-rendered="onFirstDataRendered"
          @row-data-updated="onRowDataUpdated"
        />
      </div>
    </div>

    <form class="rounded-lg border bg-card p-3 space-y-2">
      <div class="text-xs font-semibold text-foreground">{{ t('htSummary.remark') }}</div>
      <textarea
        :value="explanationText"
        rows="3"
        :placeholder="t('htSummary.placeholder')"
        readonly
        class="w-full rounded-md border bg-muted/40 px-3 py-2 text-sm text-foreground outline-none"
      />
    </form>
  </div>
</template>

<style scoped>
:deep(.ag-layout-auto-height .ag-center-cols-viewport),
:deep(.ag-layout-auto-height .ag-center-cols-container) {
  min-height: 0 !important;
}

:deep(.ht-summary-fee-row .ag-cell) {
  background: color-mix(in oklab, var(--muted) 45%, transparent);
}
</style>

