import { createHttpError } from '../../../utils/http.js'
import { normalizeProductName } from '../product-resolution-service.js'
import {
  appointCloudtentaclesVirtualNumber,
  backCloudtentaclesVirtualNumber,
  fetchCloudtentaclesVirtualNumberCode,
  generateCloudtentaclesLoginCode,
  getCloudtentaclesBindUrl,
  verifyCloudtentaclesLoginCode,
} from '../../platforms/cloudtentacles/virtual-number-service.js'
import { KUAISHOU_CLOUD_FIXED_VN_KEY, type JsonObject } from './domain.js'
import { logInfo } from '../../../utils/logger.js'

/** 账号号码配额占满后冷却：避免领取页轮询 / open-91 交付每轮都狂打上游 */
const APPOINT_QUOTA_COOLDOWN_MS = 60_000
const appointQuotaCooldowns = new Map<string, number>()

export function resolveKuaishouCloudBindingResources(
  flow: JsonObject,
  { skuItems = [], knapsackItems = [] }: { skuItems?: unknown[]; knapsackItems?: unknown[] } = {},
) {
  const normalizedSkuItems = Array.isArray(skuItems) ? skuItems.filter(isCloudSkuLikeItem) : []
  const normalizedKnapsackItems = Array.isArray(knapsackItems)
    ? knapsackItems.filter(isCloudSkuLikeItem)
    : []
  const currentSkuId = Number(flow?.binding?.skuId || 0) || 0
  const currentSkuName = String(flow?.binding?.skuName || '').trim()
  const nameCandidates = collectKuaishouCloudNameCandidates(flow)

  const skuItemById =
    currentSkuId > 0
      ? normalizedSkuItems.find((item) => Number(item.id || 0) === currentSkuId) || null
      : null
  const knapsackItemById =
    currentSkuId > 0
      ? normalizedKnapsackItems.find((item) => Number(item.id || 0) === currentSkuId) || null
      : null

  if (skuItemById || knapsackItemById) {
    const matchedItem = skuItemById || knapsackItemById
    return {
      skuId: Number(matchedItem?.id || 0) || 0,
      skuName: String(currentSkuName || matchedItem?.name || '').trim(),
      vnKey: KUAISHOU_CLOUD_FIXED_VN_KEY,
      skuItem: skuItemById,
      knapsackItem: knapsackItemById,
      resolvedByName: false,
    }
  }

  const matchedSkuItem = findCloudItemByNames(normalizedSkuItems, nameCandidates)
  const matchedKnapsackItem = findCloudItemByNames(
    normalizedKnapsackItems,
    nameCandidates,
    matchedSkuItem ? Number(matchedSkuItem.id || 0) : 0,
  )
  const matchedItem = matchedSkuItem || matchedKnapsackItem

  return {
    skuId: Number(matchedItem?.id || 0) || 0,
    skuName: String(currentSkuName || matchedItem?.name || '').trim(),
    vnKey: KUAISHOU_CLOUD_FIXED_VN_KEY,
    skuItem: matchedSkuItem,
    knapsackItem: matchedKnapsackItem,
    resolvedByName: Boolean(matchedItem),
  }
}

export function resolveKuaishouCloudVnKeyCandidates(_input: JsonObject = {}) {
  return [KUAISHOU_CLOUD_FIXED_VN_KEY]
}

export async function prepareKuaishouCloudBindResourceWithFallback(input: JsonObject = {}) {
  const { cloudContext = {}, vnKeyCandidates = [] } = input
  const candidates = Array.isArray(vnKeyCandidates) ? vnKeyCandidates : []
  const sourceKey = String(cloudContext.resolvedSourceKey || '').trim()
  let lastError = null

  for (const vnKey of candidates) {
    let vnId = 0
    let vnPhone
    const cooldownKey = `${sourceKey}|${vnKey}`

    const cooldownUntil = appointQuotaCooldowns.get(cooldownKey) || 0
    if (cooldownUntil > Date.now()) {
      throw createHttpError('账号虚拟号配额已满，请先退回已占用号码或稍后重试', {
        statusCode: 409,
        errorCode: 'cloudtentacles_vn_quota_cooldown',
      })
    }

    try {
      const appointed = await appointCloudtentaclesVirtualNumber({
        ...cloudContext,
        key: vnKey,
      })
      vnId = Number(appointed.item?.id || 0)
      vnPhone = String(appointed.item?.phone || '').trim()

      logInfo('[kuaishou-cloud/binding]', '虚拟号申请成功', {
        sourceKey,
        accountLabel: cloudContext.accountLabel,
        purpose: String(input.purpose || 'prepare_binding'),
        vnKey,
        vnId,
      })

      if (!vnId || !vnPhone) {
        throw createHttpError('申请虚拟号成功但返回数据不完整', {
          statusCode: 502,
          errorCode: 'kuaishou_cloud_invalid_vn',
        })
      }

      await generateCloudtentaclesLoginCode({
        ...cloudContext,
        key: vnKey,
        id: vnId,
      })

      const fetchedCode = await fetchCloudtentaclesVirtualNumberCode({
        ...cloudContext,
        key: vnKey,
        phone: vnPhone,
      })

      await verifyCloudtentaclesLoginCode({
        ...cloudContext,
        key: vnKey,
        id: vnId,
        code: fetchedCode.code,
      })

      const bindUrlResult = await getCloudtentaclesBindUrl({
        ...cloudContext,
        key: vnKey,
        id: vnId,
      })

      return {
        vnKey,
        vnId,
        vnPhone,
        bindUrl: String(bindUrlResult.bindUrl || '').trim(),
      }
    } catch (error) {
      lastError = error

      if (isAppointQuotaExhaustedError(error)) {
        appointQuotaCooldowns.set(cooldownKey, Date.now() + APPOINT_QUOTA_COOLDOWN_MS)
      }

      if (vnId > 0) {
        try {
          await backCloudtentaclesVirtualNumber({
            ...cloudContext,
            key: vnKey,
            id: vnId,
          })
        } catch {
          // 退号失败保留主错误
        }
      }

      if (!isRecoverableKuaishouCloudVnKeyError(error)) {
        throw error
      }
    }
  }

  throw (
    lastError ||
    createHttpError('没有找到可用的 VN Key', {
      statusCode: 409,
      errorCode: 'kuaishou_cloud_missing_binding_config',
    })
  )
}

function collectKuaishouCloudNameCandidates(flow: JsonObject) {
  return Array.from(
    new Set(
      [
        String(flow?.binding?.skuName || '').trim(),
        String(flow?.internalSkuName || '').trim(),
        String(flow?.internalSkuCode || '').trim(),
      ].filter(Boolean),
    ),
  )
}

function findCloudItemByNames(items: JsonObject[], nameCandidates: string[], preferredId = 0) {
  const normalizedItems = Array.isArray(items) ? items : []
  const normalizedNames = nameCandidates
    .map((item: string) => ({
      raw: String(item || '').trim(),
      normalized: normalizeProductName(item),
    }))
    .filter((item) => item.raw && item.normalized)

  if (normalizedNames.length === 0 || normalizedItems.length === 0) {
    return preferredId > 0
      ? normalizedItems.find((item: JsonObject) => Number(item.id || 0) === preferredId) || null
      : null
  }

  if (preferredId > 0) {
    const preferred =
      normalizedItems.find((item: JsonObject) => Number(item.id || 0) === preferredId) || null
    if (preferred) {
      return preferred
    }
  }

  const exactMatches = normalizedItems.filter((item: JsonObject) => {
    const itemName = normalizeProductName(item.name)
    return normalizedNames.some(
      (candidate: { normalized: string }) => candidate.normalized === itemName,
    )
  })
  if (exactMatches.length > 0) {
    return exactMatches[0]
  }

  const partialMatches = normalizedItems.filter((item: JsonObject) => {
    const itemName = normalizeProductName(item.name)
    return normalizedNames.some(
      (candidate: { normalized: string }) =>
        itemName.includes(candidate.normalized) || candidate.normalized.includes(itemName),
    )
  })
  if (partialMatches.length > 0) {
    return partialMatches.sort(
      (left, right) => String(left.name || '').length - String(right.name || '').length,
    )[0]
  }

  return null
}

function isCloudSkuLikeItem(item: unknown): item is JsonObject {
  const current = item && typeof item === 'object' ? (item as JsonObject) : {}
  return Number(current.id || 0) > 0
}

function isAppointQuotaExhaustedError(error: unknown) {
  const current = error && typeof error === 'object' ? (error as JsonObject) : {}
  return (
    String(current.errorCode || current.code || '').trim() === 'cloudtentacles_vn_appoint_failed' &&
    String(current.message || '')
      .trim()
      .includes('最多同时占用')
  )
}

function isRecoverableKuaishouCloudVnKeyError(error: unknown) {
  const current = error && typeof error === 'object' ? (error as JsonObject) : {}
  const errorCode = String(current.errorCode || current.code || '').trim()
  const errorMessage = String(current.message || '').trim()
  return (
    errorCode === 'cloudtentacles_vn_bind_url_failed' && errorMessage.includes('不支持的游戏类型')
  )
}
