import { roundTo } from '@/lib/decimal'
import { formatThousandsFlexible } from '@/lib/numberFormat'
import type { GridApi } from 'ag-grid-community'
import { nextTick } from 'vue'

export type ScaleBudgetCheckField = 'benchmarkBudgetBasicChecked' | 'benchmarkBudgetOptionalChecked'
export type ScaleBudgetHeaderCheckState = 'all' | 'none' | 'partial'

type BudgetCheckRow = {
  id: string
  benchmarkBudgetBasicChecked: boolean
  benchmarkBudgetOptionalChecked: boolean
  benchmarkBudgetBasic?: number | null
  benchmarkBudgetOptional?: number | null
}

export interface ScaleBudgetToggleHeaderParams {
  displayName?: string
  field: ScaleBudgetCheckField
  getHeaderCheckState?: (field: ScaleBudgetCheckField) => ScaleBudgetHeaderCheckState
  onToggleAll?: (field: ScaleBudgetCheckField, checked: boolean) => void
}

export class ScaleBudgetToggleHeader {
  private params!: ScaleBudgetToggleHeaderParams
  private eGui!: HTMLDivElement
  private checkbox!: HTMLInputElement
  private label!: HTMLSpanElement

  init(params: ScaleBudgetToggleHeaderParams) {
    this.params = params
    const root = document.createElement('div')
    root.style.display = 'inline-flex'
    root.style.alignItems = 'center'
    root.style.gap = '6px'
    root.style.width = '100%'

    const checkbox = document.createElement('input')
    checkbox.type = 'checkbox'
    checkbox.className = 'cursor-pointer'
    checkbox.addEventListener('click', event => event.stopPropagation())
    checkbox.addEventListener('mousedown', event => event.stopPropagation())
    checkbox.addEventListener('change', event => {
      event.stopPropagation()
      this.params.onToggleAll?.(this.params.field, checkbox.checked)
    })

    const label = document.createElement('span')
    label.textContent = String(params.displayName || '')
    label.style.userSelect = 'none'
    label.addEventListener('click', event => event.stopPropagation())

    root.append(checkbox, label)
    this.eGui = root
    this.checkbox = checkbox
    this.label = label
    this.applyCheckState()
  }

  getGui() {
    return this.eGui
  }

  refresh(params: ScaleBudgetToggleHeaderParams) {
    this.params = params
    this.label.textContent = String(params.displayName || '')
    this.applyCheckState()
    return true
  }

  destroy() {
    // noop
  }

  private applyCheckState() {
    const state = this.params.getHeaderCheckState?.(this.params.field) || 'none'
    this.checkbox.indeterminate = state === 'partial'
    this.checkbox.checked = state === 'all'
  }
}

export const formatScaleEditableNumber = (params: any, precision = 3, emptyText = '请输入') => {
  if (!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')) {
    return emptyText
  }
  if (params.value == null) return ''
  return formatThousandsFlexible(params.value, precision)
}

export const formatScaleEditableConditionalNumber = (
  params: any,
  options: { enabled: boolean; precision?: number; emptyText?: string }
) => {
  if (!params.node?.group && !params.node?.rowPinned && !options.enabled) {
    return ''
  }
  if (!params.node?.group && !params.node?.rowPinned && (params.value == null || params.value === '')) {
    return options.emptyText ?? '点击输入'
  }
  if (params.value == null) return ''
  return formatThousandsFlexible(params.value, options.precision ?? 3)
}

export const formatScaleReadonlyMoney = (params: any) => {
  if (params.value == null || params.value === '') return ''
  return formatThousandsFlexible(roundTo(params.value, 3), 3)
}

export const updateScaleBudgetCheckState = <TRow extends BudgetCheckRow>(
  rows: TRow[],
  rowId: string,
  checkField: ScaleBudgetCheckField,
  checked: boolean
) => {
  for (const row of rows) {
    if (row.id !== rowId) continue
    if (checkField === 'benchmarkBudgetBasicChecked') {
      row.benchmarkBudgetBasicChecked = checked
      row.benchmarkBudgetBasic = checked ? row.benchmarkBudgetBasic : 0
      return
    }
    row.benchmarkBudgetOptionalChecked = checked
    row.benchmarkBudgetOptional = checked ? row.benchmarkBudgetOptional : 0
    return
  }
}

export const createScaleBudgetCellRendererWithCheck = <TRow extends Record<string, any>>(
  checkField: ScaleBudgetCheckField,
  options: {
    formatValue: (params: any) => string
    onToggle: (row: TRow, checked: boolean) => void
  }
) => (params: any) => {
  const valueText = options.formatValue(params)
  const hasValue = params.value != null && params.value !== ''
  if (params.node?.group || params.node?.rowPinned || !params.data || !hasValue) {
    return valueText
  }

  const wrapper = document.createElement('div')
  wrapper.style.display = 'flex'
  wrapper.style.alignItems = 'center'
  wrapper.style.justifyContent = 'space-between'
  wrapper.style.gap = '6px'
  wrapper.style.width = '100%'
  wrapper.addEventListener('pointerdown', event => event.stopPropagation())
  wrapper.addEventListener('mousedown', event => event.stopPropagation())
  wrapper.addEventListener('click', event => event.stopPropagation())
  wrapper.addEventListener('dblclick', event => event.stopPropagation())

  const checkbox = document.createElement('input')
  checkbox.type = 'checkbox'
  checkbox.className = 'cursor-pointer'
  checkbox.checked = params.data[checkField] !== false
  checkbox.addEventListener('pointerdown', event => event.stopPropagation())
  checkbox.addEventListener('mousedown', event => event.stopPropagation())
  checkbox.addEventListener('click', event => event.stopPropagation())
  checkbox.addEventListener('change', event => {
    event.stopPropagation()
    const targetRow = params.data as TRow | undefined
    if (!targetRow) return
    options.onToggle(targetRow, checkbox.checked)
    void nextTick(() => {
      params.api?.redrawRows?.({
        rowNodes: params.node ? [params.node] : undefined
      })
      params.api?.refreshCells?.({
        rowNodes: params.node ? [params.node] : undefined,
        force: true
      })
    })
  })

  const valueSpan = document.createElement('span')
  valueSpan.textContent = valueText
  valueSpan.addEventListener('pointerdown', event => event.stopPropagation())
  valueSpan.addEventListener('mousedown', event => event.stopPropagation())
  valueSpan.addEventListener('click', event => event.stopPropagation())
  wrapper.append(checkbox, valueSpan)

  return wrapper
}

export const createScaleBudgetCellRendererToggleFactory = <TRow extends BudgetCheckRow>(
  getRows: () => TRow[],
  onAfterToggle: () => void
) => (checkField: ScaleBudgetCheckField) =>
  createScaleBudgetCellRendererWithCheck<TRow>(checkField, {
    formatValue: formatScaleReadonlyMoney,
    onToggle: (targetRow: TRow, checked: boolean) => {
      updateScaleBudgetCheckState(getRows(), targetRow.id, checkField, checked)
      onAfterToggle()
    }
  })

export const getScaleMergeColSpanBeforeTotal = (params: any) => {
  if (!params.node?.group && !params.node?.rowPinned) return 1
  const displayedColumns = params.api?.getAllDisplayedColumns?.()
  if (!Array.isArray(displayedColumns) || !params.column) return 1
  const currentIndex = displayedColumns.findIndex((column: any) => column.getColId() === params.column.getColId())
  const totalIndex = displayedColumns.findIndex((column: any) => column.getColId() === 'budgetFeeTotal')
  if (currentIndex < 0 || totalIndex <= currentIndex) return 1
  return totalIndex - currentIndex
}

export const refreshScaleGridAfterColumnReset = async <TRow>(gridApi: GridApi<TRow> | null | undefined) => {
  await nextTick()
  gridApi?.refreshHeader()
  gridApi?.refreshCells({ force: true })
}

export const restoreScaleColumnDefaults = async <TRow>(options: {
  gridApi: GridApi<TRow> | null | undefined
  rows: TRow[]
  getCurrentValue: (row: TRow) => number | null | undefined
  getNextValue: (row: TRow) => number | null | undefined
  isSameValue: (left: number | null | undefined, right: number | null | undefined) => boolean
  applyValue: (row: TRow, nextValue: number | null) => void
  afterApply: () => Promise<void>
}) => {
  options.gridApi?.stopEditing()
  let changed = false
  for (const row of options.rows) {
    const nextValue = options.getNextValue(row) ?? null
    if (options.isSameValue(options.getCurrentValue(row), nextValue)) continue
    options.applyValue(row, nextValue)
    changed = true
  }
  if (!changed) return false
  await options.afterApply()
  await refreshScaleGridAfterColumnReset(options.gridApi)
  return true
}
