// Lock 基类
class Lock {
  public isLock: boolean

  constructor() {
    this.isLock = false
  }

  lock(): void {
    this.isLock = true
  }

  unlock(): void {
    this.isLock = false
  }
}
import { ElMessage } from 'element-plus'
import BigNumber from 'bignumber.js'

// 定义数据类型
interface WeighItem {
  id?: string
  outWarehouseWeight?: string
  status?: string

  trackingNumber?: string
  shopNumber?: string
  platform?: string
  logisticsCompanyName?: string
  logisticsCompanyCode?: string
  logisticsCompanyId?: string
  wgap?: number | string
  weight?: string
}
interface SearchItem {
  type: string
  no: string
  logisticsCompanyCode?: string
}
interface Data {
  data: WeighItem[]
  code: number
}

type AudioKey =
  | 'weight_warning'
  | 'weight_success'
  | 'weight_repeat'
  | 'weight_search_error'
  | 'weight_search_success'
type AudioFiles = Record<AudioKey, string>

// API 响应类型

class Weigh extends Lock {
  public weightInput: boolean
  public list: WeighItem[]
  public cachedList: WeighItem[]
  public selectType: string
  private audios: AudioFiles
  private audioElements: Map<AudioKey, HTMLAudioElement>

  constructor() {
    super()
    this.weightInput = true
    this.selectType = 'trackingNumber'
    this.list = []
    this.cachedList = []
    this.audios = {
      weight_warning: new URL(
        '@/assets/audio/weight_warning.mp3',
        import.meta.url,
      ).href,
      weight_success: new URL(
        '@/assets/audio/weight_success.mp3',
        import.meta.url,
      ).href,
      weight_repeat: new URL(
        '@/assets/audio/weight_repeat.mp3',
        import.meta.url,
      ).href,
      weight_search_error: new URL(
        '@/assets/audio/weight_search_error.mp3',
        import.meta.url,
      ).href,
      weight_search_success: new URL(
        '@/assets/audio/weight_search_success.mp3',
        import.meta.url,
      ).href,
    }
    this.audioElements = new Map()
    this.preloadAudios()
  }

  // 预加载音频文件
  private preloadAudios(): void {
    Object.entries(this.audios).forEach(([key, src]) => {
      const audio = new Audio()
      audio.src = src
      audio.preload = 'auto'
      this.audioElements.set(key as AudioKey, audio)
    })
  }

  clear(): void {
    this.list = []
    this.cachedList = []
    this.weightInput = true
    this.selectType = 'trackingNumber'
  }

  stashToCache(): void {
    if (this.list.length) {
      const merged = [...this.cachedList]
      for (const item of this.list) {
        const idx = merged.findIndex(
          (m) => m.id && item.id && m.id === item.id,
        )
        if (idx >= 0) {
          merged[idx] = item
        } else {
          merged.unshift(item)
        }
      }
      this.cachedList = merged
    }
    this.list = []
    this.weightInput = true
  }

  restoreFromCache(rows: WeighItem[]): void {
    this.list = [...rows]
    this.weightInput = !this.hasPendingWeights()
  }

  syncCachedList(rows: WeighItem[]): void {
    this.cachedList = [...rows]
  }

  removeFromCache(id?: string): void {
    if (!id) return
    this.cachedList = this.cachedList.filter((el) => el.id !== id)
  }

  updatedList(data: WeighItem[]) {
    this.list = [...data]
    this.syncCachedList(data)
    this.weightInput = !this.hasPendingWeights()
  }

  private deduplicate(
    value: string,
    callback?: (list: WeighItem[]) => void,
  ): boolean {
    const existingIndex = this.list.findIndex(
      (item) =>
        item.trackingNumber === value ||
        (this.selectType === 'shopNumber' && item.shopNumber === value),
    )

    if (existingIndex !== -1) {
      const [existingItem] = this.list.splice(existingIndex, 1)
      this.list.unshift(existingItem)
      callback?.(this.list)
      this.playAudio('weight_repeat')
      return true
    }

    return false
  }

  check(
    value: string,
    apiCall: (params: SearchItem) => Promise<Data>,
    params: SearchItem,
    callback?: (list: WeighItem[]) => void,
    type?: string,
  ): void {
    this.selectType = type as string
    // 空值检查
    if (!value?.trim()) {
      this.playAudio('weight_warning')
      return
    }

    // 防止重复提交
    if (this.isLock) return

    this.lock()

    try {
      // 判断输入类型并处理
      if (this.isWeightInput(value)) {
        this.processWeightInput(value, callback)
      } else {
        this.processTrackingNumberInput(value, apiCall, params, callback)
      }
    } catch (error) {
      console.error('称重处理错误:', error)
      this.playAudio('weight_search_error')
    } finally {
      this.unlock()
    }
  }

  private isWeightInput(value: string): boolean {
    return value.length < 7
  }

  private processWeightInput(
    value: string,
    callback?: (list: WeighItem[]) => void,
  ): void {
    // 验证数字格式
    if (isNaN(Number(value)) || Number(value) <= 0) {
      this.playAudio('weight_warning', '请录入正确的重量')
      return
    }

    console.log('currentItem', this.list)

    if (!this.list.length) {
      this.playAudio(
        'weight_warning',
        `请录入${this.selectType === 'trackingNumber' ? '跟踪号' : '店铺单号'}`,
      )
      return
    }
    // 检查是否已录入重量
    if (!this.hasPendingWeights()) {
      this.playAudio(
        'weight_warning',
        `请录入${this.selectType === 'trackingNumber' ? '跟踪号' : '店铺单号'}`,
      )
      return
    }
    this.list = this.list.map((el) => {
      if (!el.outWarehouseWeight) {
        // 确保输入值也是BigNumber处理过的
        const valueNum = Number(value) || 0
        const weightNum = Number(el.weight) || 0

        const wgap = new BigNumber(valueNum)
          .minus(weightNum)
          .abs()
          .decimalPlaces(2, BigNumber.ROUND_HALF_UP) // 明确指定四舍五入规则
          .toNumber()

        return {
          ...el,
          outWarehouseWeight: value,
          wgap: wgap,
        }
      }
      return el
    })
    // 更新重量信息
    callback?.(this.list)
    this.weightInput = true
    this.playAudio('weight_success')
  }

  private async processTrackingNumberInput(
    value: string,
    apiCall: (params: SearchItem) => Promise<Data>,
    params: SearchItem,
    callback?: (list: WeighItem[]) => void,
  ): Promise<void> {
    // 检查重量录入状态
    if (!this.weightInput) {
      this.playAudio('weight_warning', '请录入重量')
      return
    }
    if (this.deduplicate(value, callback)) {
      return
    }

    const cachedItem = this.cachedList.find(
      (item) =>
        (this.selectType === 'trackingNumber' && item.trackingNumber === value) ||
        (this.selectType === 'shopNumber' && item.shopNumber === value),
    )
    if (cachedItem) {
      this.list = [cachedItem, ...this.list]
      this.weightInput = !!cachedItem.outWarehouseWeight
      callback?.(this.list)
      this.playAudio('weight_search_success')
      return
    }

    try {
      const response = await apiCall(params)
      console.log(211, response)

      const { data } = response

      if (!data.length) {
        this.playAudio('weight_search_error', '查询失败')
        return
      }

      if (this.list?.length) {
        const firstLogisticsCode = data[0]?.logisticsCompanyCode

        const hasDifferentLogistics = this.list.some(
          (el) =>
            // 如果两个代码不都是undefined，并且它们不相等
            !(
              el.logisticsCompanyCode === undefined &&
              firstLogisticsCode === undefined
            ) && el.logisticsCompanyCode !== firstLogisticsCode,
        )

        if (hasDifferentLogistics) {
          this.playAudio(
            'weight_search_error',
            '当前查询的订单不属于所选择的物流公司，请核实后再试',
          )
          return
        }
      }

      // 最终去重检查
      if (
        this.deduplicate(
          (data[0] as WeighItem).trackingNumber as string,
          callback,
        )
      ) {
        return
      }

      // 添加新项目
      this.addNewItem(data, callback)
    } catch (error) {
      console.error('跟踪号查询错误:', error)
      // this.playAudio('weight_search_error')
    }
  }

  private addNewItem(
    data: WeighItem[],
    callback?: (list: WeighItem[]) => void,
  ): void {
    const newArr = data.map((el) => {
      return {
        ...el,
        outWarehouseWeight: '',
        wgap: '',
      }
    })

    this.list = [...newArr, ...this.list]
    this.weightInput = false
    callback?.(this.list)
    this.playAudio('weight_search_success')
  }

  playAudio(key: AudioKey, message?: string): void {
    const messageMap: Record<AudioKey, string> = {
      weight_warning: `请录入${this.selectType === 'trackingNumber' ? '跟踪号' : '店铺单号'
        }或重量`,
      weight_success: '',
      weight_repeat: '重复录入',
      weight_search_error: `请录入${this.selectType === 'trackingNumber' ? '跟踪号' : '店铺单号'
        }或重量`,
      weight_search_success: '',
    }

    const displayMessage = message || messageMap[key]
    if (displayMessage) {
      console.log(displayMessage)
      ElMessage.warning(displayMessage)
    }

    // 使用预加载的音频元素
    const audio = this.audioElements.get(key)
    if (audio) {
      audio.currentTime = 0 // 重置播放位置
      audio.play().catch((error) => {
        console.warn(`音频播放失败: ${key}`, error)
      })
    }
  }

  hasPendingWeights(): boolean {
    return this.list.some((item) => !item.outWarehouseWeight)
  }

  getTotalWeight(): number {
    return this.list.reduce((total, item) => {
      return (
        total + (item.outWarehouseWeight ? Number(item.outWarehouseWeight) : 0)
      )
    }, 0)
  }
}

export default new Weigh()
