import {
  ArrayExt,
  Interp,
  NumberExt,
  ObjectExt,
  StringExt,
  Timing,
} from '../../common'
import { unitReg } from '../../common/animation/util'
import type { CamelToKebabCase } from '../../types'
import type { Cell } from '../cell'
import { isNotReservedWord } from './utils'

/**
 * Web Animation API 的 KeyframeEffect 实现，功能完善实现中
 * 参考: https://developer.mozilla.org/en-US/docs/Web/API/KeyframeEffect
 */
export class KeyframeEffect {
  private _target: Cell
  private _keyframes: Keyframe[] | PropertyIndexedKeyframes | null
  private _computedKeyframes: ComputedKeyframe[]
  private _options: KeyframeAnimationOptions
  // biome-ignore lint/suspicious/noExplicitAny: <属性类型存在多种可能>
  private _originProps?: Record<string, any> = {}

  constructor(
    target: Cell,
    keyframes: Keyframe[] | PropertyIndexedKeyframes | null,
    options?: number | KeyframeAnimationOptions,
  ) {
    this._target = target
    this._options = NumberExt.isNumber(options)
      ? { duration: options }
      : { ...options }
    this.setKeyframes(keyframes)
  }

  get target(): Cell | null {
    return this._target
  }

  getKeyframes(): ComputedKeyframe[] {
    if (!this._keyframes || this._keyframes.length === 0) return []

    // 标准化关键帧数据
    let normalizedFrames = []

    if (Array.isArray(this._keyframes)) {
      normalizedFrames = [...this._keyframes]
    }

    // 处理对象形式的关键帧,eg: {'position/x': [0, 100],'position/y': 100 }
    if (ObjectExt.isPlainObject(this._keyframes)) {
      const frameValues = Object.values(this._keyframes)
      const frameValuesArr = frameValues.map(
        (subArr) => ArrayExt.castArray(subArr).length,
      )
      const maxFramesLength = Math.max(...frameValuesArr)

      for (let i = 0; i < maxFramesLength; i++) {
        const frame = {} as ComputedKeyframe
        Object.entries(this._keyframes).forEach(([prop, value]) => {
          const v = ArrayExt.castArray(value)[i]
          if (isNotReservedWord(prop) && v != null) {
            frame[prop] = v
          }
        })
        normalizedFrames.push(frame)
      }
    }

    normalizedFrames = normalizedFrames.map((keyframe, index, arr) => {
      const frame = keyframe ?? {}
      // biome-ignore lint/suspicious/noExplicitAny: <属性类型存在多种可能>
      const normalized: Record<string, any> = {}

      normalized.offset = frame.offset
      // 确保每个关键帧都有 easing
      normalized.easing =
        frame.easing ??
        arr[index - 1]?.easing ??
        this.getComputedTiming().easing

      // 复制其他属性
      Object.keys(frame).forEach((prop) => {
        if (isNotReservedWord(prop)) {
          normalized[prop] = frame[prop]
        }
      })
      return normalized
    })

    // 计算 computedOffset
    normalizedFrames = normalizedFrames.map((frame, index) => {
      // 如果 offset 未定义或为 null，则自动计算
      if (frame.offset == null) {
        if (index === normalizedFrames.length - 1) {
          frame.computedOffset = 1
        } else if (index === 0) {
          frame.computedOffset = 0
        } else {
          // 均匀分布中间关键帧
          frame.computedOffset = index / (normalizedFrames.length - 1)
        }
      } else {
        frame.computedOffset = frame.offset
      }

      return frame
    })

    return normalizedFrames
  }

  setKeyframes(keyframes: Keyframe[] | PropertyIndexedKeyframes | null): void {
    this._keyframes = keyframes
    this._computedKeyframes = this.getKeyframes()
    // 收集动画属性的原始值
    this._computedKeyframes.forEach((frame) => {
      Object.keys(frame).forEach((prop) => {
        if (isNotReservedWord(prop) && this._originProps[prop] == null) {
          this._originProps[prop] = this.target.getPropByPath(prop)
        }
      })
    })
  }

  getTiming(): EffectTiming {
    return ObjectExt.defaults(this._options, defaultTiming)
  }

  getComputedTiming(): ComputedEffectTiming {
    const timing = this.getTiming()
    const activeDuration = timing.duration * timing.iterations

    return {
      ...timing,
      activeDuration,
      endTime: activeDuration + timing.delay,
    }
  }

  apply(iterationTime: number | null): void {
    if (!this._target || !this._computedKeyframes.length) return

    // 参数为null则回到初始状态
    if (iterationTime == null) {
      Object.entries(this._originProps).forEach(([prop, value]) => {
        this.target.setPropByPath(prop, value)
      })
      return
    }

    const timing = this.getComputedTiming()
    const duration = timing.duration
    if (duration < 0) return

    // 计算进度 (0-1)
    const progress = Math.min(iterationTime / duration, 1)

    // 找到当前进度对应的关键帧
    const frames = this._computedKeyframes
    if (frames.length === 0) return

    let startFrame = { computedOffset: 0 } as ComputedKeyframe
    let endFrame = { computedOffset: 1 } as ComputedKeyframe

    for (const frame of frames) {
      if (progress === 0 && frame.computedOffset === 0) {
        startFrame = frame
      }
      if (progress === 1 && frame.computedOffset === 1) {
        endFrame = frame
      }
      if (frame.computedOffset < progress) {
        startFrame = frame
      }
      if (frame.computedOffset > progress) {
        endFrame = frame
        break
      }
    }

    // 计算两个关键帧之间的插值
    const startOffset = startFrame.computedOffset
    const endOffset = endFrame.computedOffset
    const frameProgress = (progress - startOffset) / (endOffset - startOffset)
    const kebabEasingName = startFrame.easing ?? endFrame.easing
    const easingName = StringExt.camelCase(kebabEasingName)
    const easingFn = Timing[easingName] ?? Timing.linear

    // 应用插值后的样式
    for (const prop in { ...startFrame, ...endFrame }) {
      if (
        isNotReservedWord(prop) &&
        (startFrame[prop] != null || endFrame[prop] != null)
      ) {
        const startValue = startFrame[prop] ?? this._originProps[prop]
        const endValue = endFrame[prop] ?? this._originProps[prop]
        let interpolation: Interp.Definition<number | string>

        // TODO: rgb color
        if (String(startValue).startsWith('#')) {
          interpolation = Interp.color
        } else if (
          prop.endsWith('transform') &&
          !NumberExt.isNumber(startValue)
        ) {
          interpolation = Interp.transform
        } else if (
          unitReg.test(String(startValue)) ||
          unitReg.test(String(endValue))
        ) {
          interpolation = Interp.unit
        } else {
          interpolation = Interp.number
        }

        const interpolationFn = interpolation(startValue, endValue)

        const value = interpolationFn(easingFn(frameProgress))

        this.target.setPropByPath(prop, value)
      }
    }
  }
}

export interface EffectTiming {
  delay?: number
  direction?: 'normal' | 'reverse' | 'alternate' | 'alternate-reverse'
  duration?: number
  easing?: CamelToKebabCase<Timing.Names>
  // TODO: backwards 和 both 的初始应用效果待实现
  fill?: 'none' | 'forwards' | 'backwards' | 'both'
  iterations?: number
}

interface ComputedEffectTiming extends EffectTiming {
  activeDuration?: number
  endTime?: number
}

export interface KeyframeEffectOptions extends EffectTiming {
  /** TODO: 待实现 */
  composite?: CompositeOperation
  /** TODO: 待实现 */
  iterationComposite?: IterationCompositeOperation
}

const defaultTiming: EffectTiming = {
  delay: 0,
  direction: 'normal',
  duration: 0,
  easing: 'linear',
  fill: 'none',
  iterations: 1,
}
