const replaceStringByMap = (value: string, replacements: Map<string, string>) => {
  let nextValue = String(value ?? '')

  replacements.forEach((targetValue, sourceValue) => {
    if (!sourceValue) return
    nextValue = nextValue.split(sourceValue).join(targetValue)
  })

  return nextValue
}

const hashString = (value: string) => {
  let hash = 0

  for (let index = 0; index < value.length; index += 1) {
    hash = (hash * 31 + value.charCodeAt(index)) >>> 0
  }

  return hash.toString(36)
}

const getAssetExtension = (fileName = '') => {
  const match = String(fileName).match(/(\.[^.]+)$/)
  return match ? match[1].toLowerCase() : ''
}

const normalizeResourceAssetExtension = (extension = '') => {
  const normalized = String(extension || '').toLowerCase()
  if (['.png', '.jpg', '.jpeg', '.svg'].includes(normalized)) {
    return normalized
  }

  return '.png'
}

const getAssetBaseName = (filePath = '') => {
  const normalized = String(filePath || '').replace(/\\/g, '/')
  const fileName = normalized.split('/').pop() || normalized
  return fileName.replace(/\.[^.]+$/, '')
}

const sanitizeAssetSegment = (value = '') =>
  String(value || '')
    .replace(/[^a-zA-Z0-9_=+(){}[\]\u4e00-\u9fa5-]+/g, '_')
    .replace(/_+/g, '_')
    .replace(/^_+|_+$/g, '')

export const getImportedAssets = (appSchema: any) =>
  Array.isArray(appSchema?.assets)
    ? appSchema.assets.filter((item: any) => item?.placeholder && item?.resourceData)
    : []

export const buildImportedAssetResourceName = (asset: any) => {
  const extension = normalizeResourceAssetExtension(getAssetExtension(asset?.name || asset?.filePath || '') || '.png')
  const baseName =
    sanitizeAssetSegment(getAssetBaseName(asset?.filePath || asset?.name || 'imported_asset')) || 'imported_asset'
  const fingerprint = hashString(`${asset?.filePath || ''}:${asset?.resourceData || ''}`).slice(0, 8)

  return `${baseName}_${fingerprint}${extension}`
}

export const buildImportedAssetCreatePayload = (
  asset: any,
  resourceGroupId: number | string,
  requestMeta: { appId?: number | string; platformId?: number | string } = {}
) => ({
  name: buildImportedAssetResourceName(asset),
  description: asset?.filePath || '',
  resourceGroupId,
  resourceData: asset?.resourceData || '',
  resourceUrl: '',
  category: 'image',
  ...(requestMeta?.appId ? { appId: requestMeta.appId } : {}),
  ...(requestMeta?.platformId ? { platformId: requestMeta.platformId } : {})
})

const estimateImportedAssetPayloadSize = (payload: any) => JSON.stringify(payload || {}).length

export const splitImportedAssetCreatePayloads = (
  payloads: any[] = [],
  options: { maxBatchSize?: number; maxPayloadSize?: number } = {}
) => {
  const maxBatchSize = Math.max(1, Number(options.maxBatchSize || 5))
  const maxPayloadSize = Math.max(1, Number(options.maxPayloadSize || 1_500_000))
  const batches: any[][] = []
  let currentBatch: any[] = []
  let currentPayloadSize = 2

  payloads.forEach((payload) => {
    const payloadSize = estimateImportedAssetPayloadSize(payload)
    const nextBatchSize = currentBatch.length + 1
    const nextPayloadSize = currentPayloadSize + payloadSize + (currentBatch.length ? 1 : 0)
    const shouldStartNewBatch =
      currentBatch.length > 0 && (nextBatchSize > maxBatchSize || nextPayloadSize > maxPayloadSize)

    if (shouldStartNewBatch) {
      batches.push(currentBatch)
      currentBatch = []
      currentPayloadSize = 2
    }

    currentBatch.push(payload)
    currentPayloadSize += payloadSize + (currentBatch.length > 1 ? 1 : 0)
  })

  if (currentBatch.length) {
    batches.push(currentBatch)
  }

  return batches
}

export const replaceImportedAssetPlaceholders = (target: any, replacements: Map<string, string>) => {
  if (!target || replacements.size === 0) return target

  const walk = (value: any): any => {
    if (Array.isArray(value)) {
      return value.map((item) => walk(item))
    }

    if (typeof value === 'string') {
      return replaceStringByMap(value, replacements)
    }

    if (!value || typeof value !== 'object') {
      return value
    }

    Object.keys(value).forEach((key) => {
      value[key] = walk(value[key])
    })

    return value
  }

  return walk(target)
}

export const getImportedAssetResourceUrl = (resource: any) =>
  resource?.resourceUrl ||
  resource?.thumbnailUrl ||
  resource?.thumbnail_url ||
  resource?.url ||
  resource?.downloadUrl ||
  resource?.download_url ||
  resource?.resource_url ||
  ''

export const findImportedAssetResource = (resources: any[] = [], target: any) => {
  const normalizedName = String(target?.name || '')
  const normalizedDescription = String(target?.description || '')

  return (
    resources.find((item: any) => String(item?.name || '') === normalizedName) ||
    resources.find((item: any) => normalizedDescription && String(item?.description || '') === normalizedDescription) ||
    null
  )
}

export const normalizeImportedAssetResourceList = (response: any) => {
  if (Array.isArray(response)) return response

  const candidates = [response?.data, response?.data?.list, response?.data?.records, response?.list, response?.records]

  for (const candidate of candidates) {
    if (Array.isArray(candidate)) {
      return candidate
    }
  }

  return []
}