import { createHttpError } from '../../../utils/http.js'
import {
  getCloudtentaclesAsset,
  listCloudtentaclesSku,
  buyCloudtentaclesSku,
} from './catalog-service.js'
import { getCloudtentaclesKnapsack } from './knapsack-service.js'
import type { JsonObject } from '../../../types/json.js'
import {
  appointCloudtentaclesVirtualNumber,
  fetchCloudtentaclesVirtualNumberCode,
  generateCloudtentaclesLoginCode,
  getCloudtentaclesBindUrl,
  verifyCloudtentaclesLoginCode,
} from './virtual-number-service.js'

type FlowStepOptions = {
  retries?: number
  retryDelayMs?: number
}

export async function runCloudtentaclesFullDebugFlow(payload: JsonObject = {}) {
  const skuId = normalizePositiveInteger(payload.skuId, 0)
  const skuCount = normalizePositiveInteger(payload.skuCount, 1)
  const vnKey = String(payload.vnKey || '').trim()

  if (!skuId) {
    throw createHttpError('cloudtentacles 完整流程缺少 SKU ID', {
      statusCode: 400,
      errorCode: 'cloudtentacles_full_flow_missing_sku_id',
    })
  }

  if (!vnKey) {
    throw createHttpError('cloudtentacles 完整流程缺少 VN Key', {
      statusCode: 400,
      errorCode: 'cloudtentacles_full_flow_missing_vn_key',
    })
  }

  const beforeAsset = await runFlowStep('查询购买前余额', () => getCloudtentaclesAsset(payload))
  await sleep(350)
  const skuList = await runFlowStep('查询 SKU 列表', () => listCloudtentaclesSku(payload))
  const targetSku = skuList.items.find((item: JsonObject) => Number(item.id || 0) === skuId) || null

  if (!targetSku) {
    throw createHttpError(`cloudtentacles 未找到 SKU ${skuId}`, {
      statusCode: 404,
      errorCode: 'cloudtentacles_full_flow_sku_not_found',
    })
  }

  const requiredAsset = Number(targetSku.price || 0) * skuCount
  if (beforeAsset.asset < requiredAsset) {
    throw createHttpError(`余额不足，当前 ${beforeAsset.asset}，需要 ${requiredAsset}`, {
      statusCode: 409,
      errorCode: 'cloudtentacles_full_flow_insufficient_asset',
    })
  }

  await sleep(350)
  const beforeKnapsack = await runFlowStep('查询购买前背包', () =>
    getCloudtentaclesKnapsack(payload),
  )
  const beforeItem = findKnapsackItem(beforeKnapsack.items, skuId)

  await sleep(500)
  const buyResult = await runFlowStep(
    '购买 SKU',
    () =>
      buyCloudtentaclesSku({
        ...payload,
        id: skuId,
        count: skuCount,
      }),
    { retries: 1, retryDelayMs: 1200 },
  )

  await sleep(1200)
  const afterAsset = await runFlowStep('查询购买后余额', () => getCloudtentaclesAsset(payload), {
    retries: 2,
    retryDelayMs: 1200,
  })
  await sleep(1200)
  const afterKnapsack = await runFlowStep(
    '查询购买后背包',
    () => getCloudtentaclesKnapsack(payload),
    {
      retries: 2,
      retryDelayMs: 1200,
    },
  )
  const afterItem = findKnapsackItem(afterKnapsack.items, skuId)

  const beforeCount = Number(beforeItem?.count || 0)
  const afterCount = Number(afterItem?.count || 0)
  const knapsackIncreased = afterCount >= beforeCount + skuCount

  if (!knapsackIncreased) {
    throw createHttpError(
      `购买后背包校验失败，购买前 ${beforeCount}，购买后 ${afterCount}，期望至少 ${beforeCount + skuCount}`,
      {
        statusCode: 409,
        errorCode: 'cloudtentacles_full_flow_knapsack_not_updated',
      },
    )
  }

  await sleep(700)
  const appointed = await runFlowStep(
    '申请虚拟号',
    () =>
      appointCloudtentaclesVirtualNumber({
        ...payload,
        key: vnKey,
      }),
    { retries: 1, retryDelayMs: 1200 },
  )
  const appointedId = Number(appointed.item?.id || 0)
  const appointedPhone = String(appointed.item?.phone || '').trim()

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

  await sleep(1200)
  const generateCodeResult = await runFlowStep(
    '生成登录码',
    () =>
      generateCloudtentaclesLoginCode({
        ...payload,
        key: vnKey,
        id: appointedId,
      }),
    { retries: 1, retryDelayMs: 1200 },
  )

  await sleep(1600)
  const fetchedCode = await runFlowStep(
    '获取验证码',
    () =>
      fetchCloudtentaclesVirtualNumberCode({
        ...payload,
        key: vnKey,
        phone: appointedPhone,
      }),
    { retries: 2, retryDelayMs: 1500 },
  )

  await sleep(1000)
  const verified = await runFlowStep(
    '校验验证码',
    () =>
      verifyCloudtentaclesLoginCode({
        ...payload,
        key: vnKey,
        id: appointedId,
        code: fetchedCode.code,
      }),
    { retries: 1, retryDelayMs: 1200 },
  )

  await sleep(1000)
  const bindUrlResult = await runFlowStep(
    '获取兑换链接',
    () =>
      getCloudtentaclesBindUrl({
        ...payload,
        key: vnKey,
        id: appointedId,
      }),
    { retries: 1, retryDelayMs: 1200 },
  )

  return {
    sku: {
      id: skuId,
      count: skuCount,
      name: String(targetSku.name || '').trim(),
      price: Number(targetSku.price || 0),
      requiredAsset,
    },
    assetCheck: {
      before: beforeAsset.asset,
      after: afterAsset.asset,
      enough: beforeAsset.asset >= requiredAsset,
      spent: beforeAsset.asset - afterAsset.asset,
    },
    knapsackCheck: {
      beforeCount,
      afterCount,
      expectedIncrease: skuCount,
      increased: knapsackIncreased,
    },
    purchase: buyResult,
    virtualNumber: {
      key: vnKey,
      id: appointedId,
      phone: appointedPhone,
      appoint: appointed,
      generateLoginCode: generateCodeResult,
      fetchedCode,
      verify: verified,
    },
    bindUrl: bindUrlResult.bindUrl,
    bindUrlResult,
  }
}

function normalizePositiveInteger(value: unknown, fallback: number) {
  const parsed = Number(value)
  return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback
}

function findKnapsackItem(items: unknown, skuId: unknown) {
  return (
    (Array.isArray(items) ? items : []).find((item) => Number(item.id || 0) === Number(skuId)) ||
    null
  )
}

async function runFlowStep<T>(
  label: string,
  runner: () => Promise<T>,
  options: FlowStepOptions = {},
) {
  const rawRetries = Number(options.retries)
  const rawRetryDelayMs = Number(options.retryDelayMs)
  const retries = Number.isInteger(rawRetries) && rawRetries > 0 ? rawRetries : 0
  const retryDelayMs =
    Number.isFinite(rawRetryDelayMs) && rawRetryDelayMs > 0 ? rawRetryDelayMs : 1000

  for (let attempt = 0; attempt <= retries; attempt += 1) {
    try {
      return await runner()
    } catch (error) {
      if (attempt >= retries || !isHighFrequencyError(error)) {
        throw wrapStepError(label, error)
      }

      await sleep(retryDelayMs * (attempt + 1))
    }
  }

  throw createHttpError(`cloudtentacles ${label}失败`, {
    statusCode: 500,
    errorCode: 'cloudtentacles_full_flow_step_failed',
  })
}

function isHighFrequencyError(error: unknown) {
  const message = error instanceof Error ? error.message : String(error || '')
  return message.toLowerCase().includes('high-frequency request')
}

function wrapStepError(label: string, error: unknown) {
  const message = error instanceof Error ? error.message : String(error || '未知错误')

  if (isHighFrequencyError(error)) {
    return createHttpError(`cloudtentacles ${label}失败：请求过于频繁，请稍后重试`, {
      statusCode: 429,
      errorCode: 'cloudtentacles_rate_limited',
    })
  }

  if (error && typeof error === 'object' && 'statusCode' in error) {
    const statusCode = Number(Reflect.get(error, 'statusCode') || 500)
    const errorCode = String(
      Reflect.get(error, 'errorCode') || 'cloudtentacles_full_flow_step_failed',
    )

    return createHttpError(`cloudtentacles ${label}失败：${message}`, {
      statusCode,
      errorCode,
    })
  }

  return createHttpError(`cloudtentacles ${label}失败：${message}`, {
    statusCode: 500,
    errorCode: 'cloudtentacles_full_flow_step_failed',
  })
}

function sleep(ms: number) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms)
  })
}
