import { resolveCloudtentaclesConfig } from '../../platforms/cloudtentacles/helpers.js'
import { maskCode as maskCodeValue, maskPhone as maskPhoneValue } from '../../../utils/masking.js'
import { isTaskFinalStatus, normalizeTaskStatus, TASK_STATUS } from '../../../domain/task-status.js'
import { asJsonObject, type JsonObject } from '../../../types/json.js'

export type { JsonObject }

export const KUAISHOU_CLOUD_FIXED_VN_KEY = '1'

const KUAISHOU_CLOUD_EXECUTOR_KEYS = new Set(['kuaishou_ct_assisted', 'kuaishou-industry'])

export function isKuaishouCloudTask(task: unknown) {
  const source = task && typeof task === 'object' ? (task as JsonObject) : {}
  return KUAISHOU_CLOUD_EXECUTOR_KEYS.has(String(source.executor_key || '').trim())
}

export function isIndustryEVoucherTask(task: unknown) {
  const source = task && typeof task === 'object' ? (task as JsonObject) : {}
  return String(source.executor_key || '').trim() === 'kuaishou-industry'
}

export type KuaishouCloudDeliveryItem = {
  cloudSkuId: number
  cloudSkuName: string
  quantity: number
}

/** normalizeKuaishouCloudFlow 出口类型（履约主路径应经此 normalize） */
export type KuaishouCloudFlow = {
  configId: string
  internalSkuCode: string
  internalSkuName: string
  deliveryItems: KuaishouCloudDeliveryItem[]
  ticket: {
    code: string
    status: string
    capturedAt: unknown
    capturedBy: unknown
    verifiedAt: unknown
    oid: string
    formToken: string
    leftCount: number
    goodsTitle: string
  }
  binding: {
    prepareStatus: string
    cloudSourceKeys: string[]
    resolvedSourceKey: string
    skuId: number
    skuName: string
    vnKey: string
    vnId: number
    vnPhone: string
    bindUrl: string
    bindPreparedAt: unknown
    bindExpiresAt: unknown
    bindProbeAt: unknown
    bindProbeStatus: string
    bindProbeMessage: string
    roleName: string
    roleId: string
  }
  role: {
    status: string
    name: string
    rid: string
    refreshedAt: unknown
    errorMessage: string
    rawInfo: unknown
    defaultName: string
    defaultRid: string
    defaultCapturedAt: unknown
    defaultCaptureStatus: string
    defaultErrorMessage: string
    isDefaultRole: boolean
  }
  purchase: {
    autoBuyEnabled: boolean
    minAssetReserve: number
    usedKnapsack: boolean
    purchaseTriggered: boolean
    assetBefore: number
    assetAfter: number
    purchaseAt: unknown
    items: unknown[]
  }
  dispatch: {
    status: string
    dispatchAt: unknown
    dispatchBy: unknown
    sendType: number
    note: string
    items: unknown[]
  }
  /**
   * 发货成功瞬间冻结的身份快照。
   * claim 轮询 / 过期换号不得覆盖；后台 UID 匹配以本快照为准。
   */
  shipped: {
    expectedUid: string
    roleId: string
    roleName: string
    vnId: number
    vnPhone: string
    vnKey: string
    dispatchedAt: string | null
  } | null
  returnNumber: {
    status: string
    returnedAt: unknown
    returnedBy: unknown
    autoReturnEnabled: boolean
  }
  consume: {
    status: string
    shopId: string
    shopName: string
    autoConsumeEnabled: boolean
    consumedAt: unknown
    errorMessage: string
  }
  rebind: {
    currentAttempt: number
    history: unknown[]
  }
  notes: string
  /** 未建模扩展（mock 等）仍可落库 */
  [key: string]: unknown
}

export function normalizeKuaishouCloudFlow(value: unknown): KuaishouCloudFlow {
  const source: JsonObject = value && typeof value === 'object' ? (value as JsonObject) : {}
  const binding = asJsonObject(source.binding)
  const role = asJsonObject(source.role)
  const purchase = asJsonObject(source.purchase)
  const dispatch = asJsonObject(source.dispatch)
  const returnNumber = asJsonObject(source.returnNumber)
  const consume = asJsonObject(source.consume)
  const ticket = asJsonObject(source.ticket)
  const rebind = asJsonObject(source.rebind)
  const shippedSource = asJsonObject(source.shipped)
  const deliveryItems = normalizeDeliveryItems(source, binding)

  const roleName = String(role.name || binding.roleName || '').trim()
  const roleId = String(role.rid || binding.roleId || '').trim()
  const defaultRoleName = String(role.defaultName || role.defaultRoleName || '').trim()
  const defaultRoleId = String(role.defaultRid || role.defaultRoleId || '').trim()
  const hasDefaultRole = Boolean(defaultRoleName || defaultRoleId)
  const isDefaultRole =
    Boolean(roleName || roleId) &&
    hasDefaultRole &&
    isSameKuaishouCloudRoleIdentity(
      { name: roleName, rid: roleId },
      { name: defaultRoleName, rid: defaultRoleId },
    )
  const bindPreparedAt = binding.bindPreparedAt || null
  const bindExpiresAt =
    binding.bindExpiresAt || resolveKuaishouCloudBindUrlExpiresAt(bindPreparedAt)

  return {
    ...source,
    configId: String(source.configId || '').trim(),
    internalSkuCode: String(source.internalSkuCode || '').trim(),
    internalSkuName: String(source.internalSkuName || '').trim(),
    deliveryItems,
    ticket: {
      code: String(ticket.code || '').trim(),
      status: String(ticket.status || 'pending').trim() || 'pending',
      capturedAt: ticket.capturedAt || null,
      capturedBy: ticket.capturedBy || null,
      verifiedAt: ticket.verifiedAt || null,
      oid: String(ticket.oid || '').trim(),
      formToken: String(ticket.formToken || '').trim(),
      leftCount: Number(ticket.leftCount || 0) || 0,
      goodsTitle: String(ticket.goodsTitle || '').trim(),
    },
    binding: {
      prepareStatus: String(binding.prepareStatus || 'pending').trim() || 'pending',
      cloudSourceKeys: normalizeStringArray(binding.cloudSourceKeys),
      resolvedSourceKey: String(binding.resolvedSourceKey || '').trim(),
      skuId: Number(binding.skuId || 0) || 0,
      skuName: String(binding.skuName || '').trim(),
      vnKey:
        String(binding.vnKey || KUAISHOU_CLOUD_FIXED_VN_KEY).trim() || KUAISHOU_CLOUD_FIXED_VN_KEY,
      vnId: Number(binding.vnId || 0) || 0,
      vnPhone: String(binding.vnPhone || '').trim(),
      bindUrl: String(binding.bindUrl || '').trim(),
      bindPreparedAt,
      bindExpiresAt,
      bindProbeAt: binding.bindProbeAt || null,
      bindProbeStatus: String(binding.bindProbeStatus || '').trim(),
      bindProbeMessage: String(binding.bindProbeMessage || '').trim(),
      roleName,
      roleId,
    },
    role: {
      status: String(role.status || (roleName || roleId ? 'ready' : 'pending')).trim() || 'pending',
      name: roleName,
      rid: roleId,
      refreshedAt: role.refreshedAt || null,
      errorMessage: String(role.errorMessage || '').trim(),
      rawInfo: role.rawInfo && typeof role.rawInfo === 'object' ? role.rawInfo : null,
      defaultName: defaultRoleName,
      defaultRid: defaultRoleId,
      defaultCapturedAt: role.defaultCapturedAt || null,
      defaultCaptureStatus: String(role.defaultCaptureStatus || '').trim(),
      defaultErrorMessage: String(role.defaultErrorMessage || '').trim(),
      isDefaultRole,
    },
    purchase: {
      autoBuyEnabled: purchase.autoBuyEnabled !== false,
      minAssetReserve: Number(purchase.minAssetReserve || 0) || 0,
      usedKnapsack: purchase.usedKnapsack === true,
      purchaseTriggered: purchase.purchaseTriggered === true,
      assetBefore: Number(purchase.assetBefore || 0) || 0,
      assetAfter: Number(purchase.assetAfter || 0) || 0,
      purchaseAt: purchase.purchaseAt || null,
      items: Array.isArray(purchase.items) ? purchase.items : [],
    },
    dispatch: {
      status: String(dispatch.status || 'pending').trim() || 'pending',
      dispatchAt: dispatch.dispatchAt || null,
      dispatchBy: dispatch.dispatchBy || null,
      sendType: Number(dispatch.sendType || 0) || 0,
      note: String(dispatch.note || '').trim(),
      items: Array.isArray(dispatch.items) ? dispatch.items : [],
    },
    shipped: normalizeKuaishouCloudShippedSnapshot(shippedSource),
    returnNumber: {
      status: String(returnNumber.status || 'pending').trim() || 'pending',
      returnedAt: returnNumber.returnedAt || null,
      returnedBy: returnNumber.returnedBy || null,
      autoReturnEnabled: returnNumber.autoReturnEnabled === true,
    },
    consume: {
      status: String(consume.status || 'pending').trim() || 'pending',
      shopId: String(consume.shopId || '').trim(),
      shopName: String(consume.shopName || '').trim(),
      autoConsumeEnabled: consume.autoConsumeEnabled === true,
      consumedAt: consume.consumedAt || null,
      errorMessage: String(consume.errorMessage || '').trim(),
    },
    rebind: {
      currentAttempt: Math.max(0, Number(rebind.currentAttempt || 0) || 0),
      history: Array.isArray(rebind.history) ? rebind.history : [],
    },
    notes: String(source.notes || '').trim(),
  }
}

export function hasKuaishouCloudDefaultRoleSnapshot(flowLike: unknown) {
  const flow = normalizeKuaishouCloudFlow(flowLike)
  return Boolean(flow.role.defaultName || flow.role.defaultRid)
}

export type KuaishouCloudShippedSnapshot = NonNullable<KuaishouCloudFlow['shipped']>

export function normalizeKuaishouCloudShippedSnapshot(
  value: unknown,
): KuaishouCloudShippedSnapshot | null {
  const source = asJsonObject(value)
  const roleId = String(source.roleId || source.rid || '').trim()
  const expectedUid = String(source.expectedUid || '').trim()
  const vnId = Number(source.vnId || 0) || 0
  const dispatchedAt = source.dispatchedAt ? String(source.dispatchedAt) : null

  // 至少要有发货角色或 UID 之一，否则视为无快照
  if (!roleId && !expectedUid && !vnId && !dispatchedAt) {
    return null
  }

  return {
    expectedUid,
    roleId,
    roleName: String(source.roleName || source.name || '').trim(),
    vnId,
    vnPhone: String(source.vnPhone || '').trim(),
    vnKey:
      String(source.vnKey || KUAISHOU_CLOUD_FIXED_VN_KEY).trim() || KUAISHOU_CLOUD_FIXED_VN_KEY,
    dispatchedAt,
  }
}

export function isKuaishouCloudDispatchSucceeded(flowLike: unknown): boolean {
  const flow = normalizeKuaishouCloudFlow(flowLike)
  return String(flow.dispatch.status || '').trim() === 'success'
}

/**
 * 发货后禁止再改绑定资源/角色（claim 轮询换号、刷角色、过期重取等）。
 * 以 dispatch.success 为准（兼容状态机被污染回 waiting_binding 的脏单）。
 */
export function isKuaishouCloudBindingMutationFrozen(
  task: { task_status?: unknown } | null | undefined,
  flowLike?: unknown,
): boolean {
  const status = normalizeTaskStatus(task?.task_status)
  if (
    status === TASK_STATUS.REDEEMING ||
    status === TASK_STATUS.DISPATCHED_PENDING_RETURN ||
    status === TASK_STATUS.COMPLETED ||
    status === TASK_STATUS.REDEEMED ||
    isTaskFinalStatus(status)
  ) {
    return true
  }

  if (flowLike !== undefined) {
    return isKuaishouCloudDispatchSucceeded(flowLike)
  }

  return false
}

export function buildKuaishouCloudShippedSnapshot(input: {
  expectedUid?: unknown
  roleId?: unknown
  roleName?: unknown
  vnId?: unknown
  vnPhone?: unknown
  vnKey?: unknown
  dispatchedAt?: unknown
}): KuaishouCloudShippedSnapshot {
  return {
    expectedUid: String(input.expectedUid || '').trim(),
    roleId: String(input.roleId || '').trim(),
    roleName: String(input.roleName || '').trim(),
    vnId: Number(input.vnId || 0) || 0,
    vnPhone: String(input.vnPhone || '').trim(),
    vnKey: String(input.vnKey || KUAISHOU_CLOUD_FIXED_VN_KEY).trim() || KUAISHOU_CLOUD_FIXED_VN_KEY,
    dispatchedAt: input.dispatchedAt ? String(input.dispatchedAt) : null,
  }
}

/**
 * 是否已有可展示的绑定角色信息（诊断用）。
 *
 * 注意：履约主闸已改为 claimIdentity.expectedUid 匹配；
 * 本函数不再要求「当前角色必须不同于虚拟机默认角色」。
 */
export function hasKuaishouCloudCustomerRole(flowLike: unknown) {
  const flow = normalizeKuaishouCloudFlow(flowLike)
  if (!flow.binding.vnPhone) {
    return false
  }

  return Boolean(flow.binding.roleName || flow.binding.roleId)
}

export function isSameKuaishouCloudRoleIdentity(
  left: { name?: unknown; rid?: unknown },
  right: { name?: unknown; rid?: unknown },
) {
  const leftRid = String(left.rid || '').trim()
  const rightRid = String(right.rid || '').trim()
  if (leftRid && rightRid) {
    return leftRid === rightRid
  }

  const leftName = normalizeRoleNameForCompare(left.name)
  const rightName = normalizeRoleNameForCompare(right.name)
  return Boolean(leftName && rightName && leftName === rightName)
}

function normalizeRoleNameForCompare(value: unknown) {
  return String(value || '')
    .trim()
    .toLowerCase()
    .replace(/\s+/g, '')
}

export function normalizeKuaishouCloudDeliveryItems(value: unknown) {
  const source: JsonObject = value && typeof value === 'object' ? (value as JsonObject) : {}
  const binding = asJsonObject(source.binding)
  return normalizeDeliveryItems(source, binding)
}

export function normalizeKuaishouCloudRoleInfo(value: unknown) {
  const rawInfo: JsonObject | null =
    value && typeof value === 'object' ? (value as JsonObject) : null
  const nestedBindInfo =
    rawInfo?.sBindInfo && typeof rawInfo.sBindInfo === 'object' ? rawInfo.sBindInfo : null
  const source = nestedBindInfo || rawInfo

  return {
    name: String(
      source?.name || source?.roleName || source?.nickname || source?.sRoleName || '',
    ).trim(),
    rid: String(
      source?.rid || source?.roleId || source?.uid || source?.sRoleId || source?.sUserId || '',
    ).trim(),
    rawInfo,
  }
}

export function maskPhone(value: unknown) {
  return maskPhoneValue(value)
}

export function maskCode(value: unknown) {
  return maskCodeValue(value)
}

export function resolveKuaishouCloudBindUrlExpiresAt(preparedAt: unknown) {
  const preparedTime = Date.parse(String(preparedAt || ''))
  if (!Number.isFinite(preparedTime)) {
    return null
  }

  const config = resolveCloudtentaclesConfig()
  const ttlSeconds = Number(config.bindUrlTtlSeconds || 600)
  return new Date(preparedTime + Math.max(1, ttlSeconds) * 1000).toISOString()
}

export function isKuaishouCloudBindUrlFresh(flow: unknown, now = new Date()) {
  const normalizedFlow = normalizeKuaishouCloudFlow(flow)
  if (!normalizedFlow.binding.bindUrl) {
    return false
  }

  const expiresAt = normalizedFlow.binding.bindExpiresAt
  if (!expiresAt) {
    return false
  }

  const expiresTime = Date.parse(String(expiresAt || ''))
  if (!Number.isFinite(expiresTime)) {
    return false
  }

  return expiresTime > now.getTime()
}

export function normalizeStringArray(value: unknown) {
  if (Array.isArray(value)) {
    return value.map((v) => String(v || '').trim()).filter(Boolean)
  }
  if (typeof value === 'string') {
    return value
      .split(',')
      .map((v) => v.trim())
      .filter(Boolean)
  }
  return []
}

function normalizeDeliveryItems(source: JsonObject, binding: JsonObject) {
  const rawItems = Array.isArray(source.deliveryItems) ? source.deliveryItems : []
  const items = rawItems.map((item) => normalizeDeliveryItem(item)).filter(Boolean) as Array<{
    cloudSkuId: number
    cloudSkuName: string
    quantity: number
  }>

  if (items.length > 0) {
    return mergeDeliveryItems(items)
  }

  const skuId = Number(binding.skuId || 0) || 0
  if (!skuId) {
    return []
  }

  return [
    {
      cloudSkuId: skuId,
      cloudSkuName: String(binding.skuName || '').trim(),
      quantity: 1,
    },
  ]
}

function normalizeDeliveryItem(value: unknown) {
  const source = value && typeof value === 'object' ? (value as JsonObject) : {}
  const cloudSkuId = Number(source.cloudSkuId || source.skuId || 0) || 0
  if (!Number.isInteger(cloudSkuId) || cloudSkuId <= 0) {
    return null
  }

  const quantity = Number(source.quantity || 1) || 1
  return {
    cloudSkuId,
    cloudSkuName: String(source.cloudSkuName || source.skuName || '').trim(),
    quantity: Number.isInteger(quantity) && quantity > 0 ? quantity : 1,
  }
}

function mergeDeliveryItems(
  items: Array<{ cloudSkuId: number; cloudSkuName: string; quantity: number }>,
) {
  const merged = new Map<number, { cloudSkuId: number; cloudSkuName: string; quantity: number }>()

  for (const item of items) {
    const existing = merged.get(item.cloudSkuId)
    if (existing) {
      existing.quantity += item.quantity
      existing.cloudSkuName = existing.cloudSkuName || item.cloudSkuName
      continue
    }

    merged.set(item.cloudSkuId, { ...item })
  }

  return Array.from(merged.values())
}
