const {
  formatMagnitudeNumber,
  getOption,
  normalizeIndex,
  parseLooseNumber
} = require('../calculator-helpers.js')

const SQRT3 = Math.sqrt(3)
const DEG_PER_RAD = 180 / Math.PI
const RAD_PER_DEG = Math.PI / 180

const CONNECTION_OPTIONS = [
  { key: 'star', label: '星形' },
  { key: 'delta', label: '三角形' }
]

const ROW_OPTIONS = [
  { key: 'lineVoltage', label: '线电压 UL', unit: 'V', placeholder: '380', editable: true, field: 'threePhaseLineVoltage' },
  { key: 'lineCurrent', label: '线电流 IL', unit: 'A', placeholder: '10', editable: true, field: 'threePhaseLineCurrent' },
  { key: 'phaseVoltage', label: '相电压 UP', unit: 'V', placeholder: '220', editable: true, field: 'threePhasePhaseVoltage' },
  { key: 'phaseCurrent', label: '相电流 IP', unit: 'A', placeholder: '10', editable: true, field: 'threePhasePhaseCurrent' },
  { key: 'apparentPower', label: '视在功率 S', unit: 'VA', placeholder: '6580', editable: true, field: 'threePhaseApparentPower' },
  { key: 'activePower', label: '实际功率 P', unit: 'W', placeholder: '5000', editable: true, field: 'threePhaseActivePower' },
  { key: 'reactivePower', label: '无功功率 Q', unit: 'var', placeholder: '3000', editable: true, field: 'threePhaseReactivePower' },
  { key: 'powerFactor', label: '功率因素 PF', unit: '', placeholder: '0.85', editable: true, field: 'threePhasePowerFactor' },
  { key: 'phaseAngle', label: '相位角 φ', unit: '°', placeholder: '31.8', editable: true, field: 'threePhasePhaseAngle' }
]

const ELECTRICAL_INPUT_KEYS = [
  'threePhaseLineVoltage',
  'threePhaseLineCurrent',
  'threePhasePhaseVoltage',
  'threePhasePhaseCurrent',
  'threePhaseApparentPower'
]
const POWER_INPUT_KEYS = [
  'threePhaseActivePower',
  'threePhaseReactivePower',
  'threePhasePowerFactor',
  'threePhasePhaseAngle'
]
const INPUT_KEYS = ELECTRICAL_INPUT_KEYS.concat(POWER_INPUT_KEYS)
const POWER_DRIVER_KEYS = [
  'threePhaseActivePower',
  'threePhaseReactivePower',
  'threePhasePowerFactor',
  'threePhasePhaseAngle'
]

function formatNumber(value) {
  return formatMagnitudeNumber(value, { fallbackText: '--' })
}

function getSignFrom(values) {
  if (Number.isFinite(values.reactivePower) && values.reactivePower !== 0) {
    return values.reactivePower < 0 ? -1 : 1
  }
  if (Number.isFinite(values.phaseAngle) && values.phaseAngle !== 0) {
    return values.phaseAngle < 0 ? -1 : 1
  }

  return 1
}

function getPreferredPowerKey(source, values) {
  const preferred = source.threePhasePowerDriver
  if (POWER_DRIVER_KEYS.includes(preferred)) {
    const valueName = {
      threePhaseActivePower: 'activePower',
      threePhaseReactivePower: 'reactivePower',
      threePhasePowerFactor: 'powerFactor',
      threePhasePhaseAngle: 'phaseAngle'
    }[preferred]

    if (Number.isFinite(values[valueName])) return preferred
  }

  return POWER_DRIVER_KEYS.find((key) => {
    const valueName = {
      threePhaseActivePower: 'activePower',
      threePhaseReactivePower: 'reactivePower',
      threePhasePowerFactor: 'powerFactor',
      threePhasePhaseAngle: 'phaseAngle'
    }[key]

    return Number.isFinite(values[valueName])
  }) || ''
}

function deriveFromKnownS(values, preferredKey) {
  const result = {
    activePower: values.activePower,
    phaseAngle: values.phaseAngle,
    powerFactor: values.powerFactor,
    reactivePower: values.reactivePower
  }
  const apparentPower = values.apparentPower
  const qSign = getSignFrom(values)
  const driver = preferredKey || getPreferredPowerKey({}, values)

  if (!Number.isFinite(apparentPower)) return result

  if (driver === 'threePhaseActivePower' && Number.isFinite(values.activePower)) {
    if (values.activePower > apparentPower) return { ...result, errorText: '实际功率不能大于视在功率' }

    result.powerFactor = apparentPower === 0 ? 0 : values.activePower / apparentPower
    result.reactivePower = qSign * Math.sqrt(Math.max(0, apparentPower * apparentPower - values.activePower * values.activePower))
    result.phaseAngle = Math.atan2(result.reactivePower, values.activePower) * DEG_PER_RAD
    return result
  }

  if (driver === 'threePhaseReactivePower' && Number.isFinite(values.reactivePower)) {
    if (Math.abs(values.reactivePower) > apparentPower) return { ...result, errorText: '无功功率绝对值不能大于视在功率' }

    result.activePower = Math.sqrt(Math.max(0, apparentPower * apparentPower - values.reactivePower * values.reactivePower))
    result.powerFactor = apparentPower === 0 ? 0 : result.activePower / apparentPower
    result.phaseAngle = Math.atan2(values.reactivePower, result.activePower) * DEG_PER_RAD
    return result
  }

  if (driver === 'threePhasePowerFactor' && Number.isFinite(values.powerFactor)) {
    result.activePower = apparentPower * values.powerFactor
    result.reactivePower = qSign * apparentPower * Math.sqrt(Math.max(0, 1 - values.powerFactor * values.powerFactor))
    result.phaseAngle = Math.atan2(result.reactivePower, result.activePower) * DEG_PER_RAD
    return result
  }

  if (driver === 'threePhasePhaseAngle' && Number.isFinite(values.phaseAngle)) {
    const angleRad = values.phaseAngle * RAD_PER_DEG
    result.powerFactor = Math.cos(angleRad)
    result.activePower = apparentPower * result.powerFactor
    result.reactivePower = apparentPower * Math.sin(angleRad)
    return result
  }

  return result
}

function derivePowerTriangle(values, preferredKey) {
  if (Number.isFinite(values.apparentPower)) {
    return deriveFromKnownS(values, preferredKey)
  }

  const result = {
    activePower: values.activePower,
    apparentPower: null,
    phaseAngle: values.phaseAngle,
    powerFactor: values.powerFactor,
    reactivePower: values.reactivePower
  }
  const p = values.activePower
  const q = values.reactivePower
  const pf = values.powerFactor
  const angle = values.phaseAngle

  if (Number.isFinite(p) && Number.isFinite(q)) {
    result.apparentPower = Math.sqrt(p * p + q * q)
    result.powerFactor = result.apparentPower === 0 ? 0 : p / result.apparentPower
    result.phaseAngle = Math.atan2(q, p) * DEG_PER_RAD
    return result
  }

  if (Number.isFinite(p) && Number.isFinite(pf)) {
    if (pf <= 0 && p > 0) return { ...result, errorText: '功率因素为 0 时实际功率只能为 0' }

    result.apparentPower = pf === 0 ? 0 : p / pf
    result.reactivePower = getSignFrom(values) * Math.sqrt(Math.max(0, result.apparentPower * result.apparentPower - p * p))
    result.phaseAngle = Math.atan2(result.reactivePower, p) * DEG_PER_RAD
    return result
  }

  if (Number.isFinite(p) && Number.isFinite(angle)) {
    const angleRad = angle * RAD_PER_DEG
    const cosValue = Math.cos(angleRad)
    if (cosValue <= 0 && p > 0) return { ...result, errorText: '相位角需小于 90° 才能由实际功率反推' }

    result.powerFactor = cosValue
    result.apparentPower = cosValue === 0 ? 0 : p / cosValue
    result.reactivePower = p * Math.tan(angleRad)
    return result
  }

  if (Number.isFinite(q) && Number.isFinite(pf)) {
    const reactiveRatio = Math.sqrt(Math.max(0, 1 - pf * pf))
    if (reactiveRatio === 0 && q !== 0) return { ...result, errorText: '功率因素为 1 时无功功率应为 0' }

    result.apparentPower = reactiveRatio === 0 ? 0 : Math.abs(q) / reactiveRatio
    result.activePower = result.apparentPower * pf
    result.phaseAngle = Math.atan2(q, result.activePower) * DEG_PER_RAD
    return result
  }

  if (Number.isFinite(q) && Number.isFinite(angle)) {
    const tanValue = Math.tan(angle * RAD_PER_DEG)
    if (Math.abs(tanValue) < 1e-12 && q !== 0) return { ...result, errorText: '相位角为 0° 时无功功率应为 0' }

    result.activePower = tanValue === 0 ? 0 : q / tanValue
    if (result.activePower < 0) return { ...result, errorText: '无功功率与相位角方向不一致' }

    result.apparentPower = Math.sqrt(result.activePower * result.activePower + q * q)
    result.powerFactor = result.apparentPower === 0 ? 0 : result.activePower / result.apparentPower
    return result
  }

  return result
}

function validate(values) {
  if ([values.lineVoltage, values.lineCurrent, values.phaseVoltage, values.phaseCurrent, values.apparentPower, values.activePower, values.reactivePower, values.powerFactor, values.phaseAngle]
    .some((value) => Number.isNaN(value))) {
    return '输入值格式无效'
  }
  if (Number.isFinite(values.lineVoltage) && values.lineVoltage <= 0) return '线电压需大于 0'
  if (Number.isFinite(values.lineCurrent) && values.lineCurrent <= 0) return '线电流需大于 0'
  if (Number.isFinite(values.phaseVoltage) && values.phaseVoltage <= 0) return '相电压需大于 0'
  if (Number.isFinite(values.phaseCurrent) && values.phaseCurrent <= 0) return '相电流需大于 0'
  if (Number.isFinite(values.apparentPower) && values.apparentPower < 0) return '视在功率不能为负数'
  if (Number.isFinite(values.activePower) && values.activePower < 0) return '实际功率不能为负数'
  if (Number.isFinite(values.powerFactor) && (values.powerFactor < 0 || values.powerFactor > 1)) return '功率因素范围为 0-1'
  if (Number.isFinite(values.phaseAngle) && Math.abs(values.phaseAngle) > 90) return '相位角范围为 -90° 到 90°'

  return ''
}

function assignVoltageFromLine(result, connectionKey, lineVoltage) {
  result.lineVoltage = lineVoltage
  result.phaseVoltage = connectionKey === 'star' ? lineVoltage / SQRT3 : lineVoltage
}

function assignVoltageFromPhase(result, connectionKey, phaseVoltage) {
  result.phaseVoltage = phaseVoltage
  result.lineVoltage = connectionKey === 'star' ? phaseVoltage * SQRT3 : phaseVoltage
}

function assignCurrentFromLine(result, connectionKey, lineCurrent) {
  result.lineCurrent = lineCurrent
  result.phaseCurrent = connectionKey === 'star' ? lineCurrent : lineCurrent / SQRT3
}

function assignCurrentFromPhase(result, connectionKey, phaseCurrent) {
  result.phaseCurrent = phaseCurrent
  result.lineCurrent = connectionKey === 'star' ? phaseCurrent : phaseCurrent * SQRT3
}

function resolveElectricalValues(connectionKey, values, preferredKey = '') {
  const result = {
    apparentPower: Number.isFinite(values.apparentPower) ? values.apparentPower : null,
    lineCurrent: null,
    lineVoltage: null,
    phaseCurrent: null,
    phaseVoltage: null
  }

  if (preferredKey === 'threePhasePhaseVoltage' && Number.isFinite(values.phaseVoltage)) {
    assignVoltageFromPhase(result, connectionKey, values.phaseVoltage)
  } else if (preferredKey === 'threePhaseLineVoltage' && Number.isFinite(values.lineVoltage)) {
    assignVoltageFromLine(result, connectionKey, values.lineVoltage)
  } else if (Number.isFinite(values.lineVoltage)) {
    assignVoltageFromLine(result, connectionKey, values.lineVoltage)
  } else if (Number.isFinite(values.phaseVoltage)) {
    assignVoltageFromPhase(result, connectionKey, values.phaseVoltage)
  }

  if (preferredKey === 'threePhasePhaseCurrent' && Number.isFinite(values.phaseCurrent)) {
    assignCurrentFromPhase(result, connectionKey, values.phaseCurrent)
  } else if (preferredKey === 'threePhaseLineCurrent' && Number.isFinite(values.lineCurrent)) {
    assignCurrentFromLine(result, connectionKey, values.lineCurrent)
  } else if (Number.isFinite(values.lineCurrent)) {
    assignCurrentFromLine(result, connectionKey, values.lineCurrent)
  } else if (Number.isFinite(values.phaseCurrent)) {
    assignCurrentFromPhase(result, connectionKey, values.phaseCurrent)
  }

  if (!Number.isFinite(result.apparentPower) && Number.isFinite(result.lineVoltage) && Number.isFinite(result.lineCurrent)) {
    result.apparentPower = SQRT3 * result.lineVoltage * result.lineCurrent
  }
  if (!Number.isFinite(result.lineCurrent) && Number.isFinite(result.apparentPower) && Number.isFinite(result.lineVoltage) && result.lineVoltage > 0) {
    assignCurrentFromLine(result, connectionKey, result.apparentPower / (SQRT3 * result.lineVoltage))
  }
  if (!Number.isFinite(result.lineVoltage) && Number.isFinite(result.apparentPower) && Number.isFinite(result.lineCurrent) && result.lineCurrent > 0) {
    assignVoltageFromLine(result, connectionKey, result.apparentPower / (SQRT3 * result.lineCurrent))
  }

  return result
}

function formatEditableValue(value) {
  return Number.isFinite(value) ? formatNumber(value) : ''
}

function buildRows(connectionKey, values, powerResult, preferredElectricalKey = '') {
  const electricalValues = resolveElectricalValues(connectionKey, {
    ...values,
    apparentPower: Number.isFinite(values.apparentPower) ? values.apparentPower : powerResult.apparentPower
  }, preferredElectricalKey)
  const displayValues = {
    activePower: Number.isFinite(powerResult.activePower) ? powerResult.activePower : values.activePower,
    apparentPower: electricalValues.apparentPower,
    lineCurrent: electricalValues.lineCurrent,
    lineVoltage: electricalValues.lineVoltage,
    phaseAngle: Number.isFinite(powerResult.phaseAngle) ? powerResult.phaseAngle : values.phaseAngle,
    phaseCurrent: electricalValues.phaseCurrent,
    phaseVoltage: electricalValues.phaseVoltage,
    powerFactor: Number.isFinite(powerResult.powerFactor) ? powerResult.powerFactor : values.powerFactor,
    reactivePower: Number.isFinite(powerResult.reactivePower) ? powerResult.reactivePower : values.reactivePower
  }
  const displayText = {
    activePower: formatEditableValue(displayValues.activePower),
    apparentPower: formatEditableValue(displayValues.apparentPower),
    lineCurrent: formatEditableValue(displayValues.lineCurrent),
    lineVoltage: formatEditableValue(displayValues.lineVoltage),
    phaseAngle: formatEditableValue(displayValues.phaseAngle),
    phaseCurrent: formatEditableValue(displayValues.phaseCurrent),
    phaseVoltage: formatEditableValue(displayValues.phaseVoltage),
    powerFactor: formatEditableValue(displayValues.powerFactor),
    reactivePower: formatEditableValue(displayValues.reactivePower)
  }

  return ROW_OPTIONS.map((row) => ({
    ...row,
    value: displayText[row.key] || (row.editable ? '' : '--')
  }))
}

function buildState(source = {}) {
  const connectionIndex = normalizeIndex(source.threePhaseConnectionIndex, CONNECTION_OPTIONS, 0)
  const connection = getOption(CONNECTION_OPTIONS, connectionIndex)
  const rawValues = INPUT_KEYS.reduce((result, key) => {
    result[key] = String(source[key] === undefined || source[key] === null ? '' : source[key])
    return result
  }, {})
  const values = {
    activePower: parseLooseNumber(rawValues.threePhaseActivePower),
    apparentPower: parseLooseNumber(rawValues.threePhaseApparentPower),
    lineCurrent: parseLooseNumber(rawValues.threePhaseLineCurrent),
    lineVoltage: parseLooseNumber(rawValues.threePhaseLineVoltage),
    phaseAngle: parseLooseNumber(rawValues.threePhasePhaseAngle),
    phaseCurrent: parseLooseNumber(rawValues.threePhasePhaseCurrent),
    phaseVoltage: parseLooseNumber(rawValues.threePhasePhaseVoltage),
    powerFactor: parseLooseNumber(rawValues.threePhasePowerFactor),
    reactivePower: parseLooseNumber(rawValues.threePhaseReactivePower)
  }

  const validationError = validate(values)
  const preferredElectricalKey = ELECTRICAL_INPUT_KEYS.includes(source.threePhaseElectricalDriver)
    ? source.threePhaseElectricalDriver
    : ''
  const electricalValues = validationError
    ? {}
    : resolveElectricalValues(connection.key, values, preferredElectricalKey)
  const preferredPowerKey = getPreferredPowerKey(source, values)
  const powerResult = validationError
    ? {}
    : derivePowerTriangle({
      ...values,
      ...electricalValues
    }, preferredPowerKey)
  const errorText = validationError || powerResult.errorText || ''

  return {
    ...rawValues,
    threePhaseConnectionIndex: connectionIndex,
    threePhaseConnectionKey: connection.key,
    threePhaseConnectionOptions: CONNECTION_OPTIONS,
    threePhaseElectricalDriver: preferredElectricalKey,
    threePhaseErrorText: errorText,
    threePhasePowerDriver: preferredPowerKey,
    threePhaseRows: buildRows(connection.key, values, powerResult || {}, preferredElectricalKey)
  }
}

function createInitialState() {
  return buildState({})
}

function updateState(state, changedData = {}) {
  return buildState({
    ...state,
    ...changedData
  })
}

function clearInputs(state = {}) {
  return updateState(state, INPUT_KEYS.reduce((result, key) => {
    result[key] = ''
    return result
  }, {
    threePhaseElectricalDriver: '',
    threePhasePowerDriver: ''
  }))
}

module.exports = {
  CONNECTION_OPTIONS,
  ELECTRICAL_INPUT_KEYS,
  POWER_DRIVER_KEYS,
  clearInputs,
  createInitialState,
  updateState
}
