import { getCloudtentaclesAsset } from '../platforms/cloudtentacles/catalog-service.js'
import type { JsonObject } from '../../types/json.js'
import {
  normalizeCloudtentaclesDeviceId,
  normalizeCloudtentaclesDeviceType,
} from '../platforms/cloudtentacles/defaults.js'
import {
  getCloudtentaclesSourceByKey,
  listCloudtentaclesSources,
} from '../platforms/cloudtentacles/source-config-service.js'
import { getCloudtentaclesSessionStateByKey } from '../platforms/cloudtentacles/session-state-service.js'
import {
  notifyCloudtentaclesAssetLow,
  notifyCloudtentaclesAuthExpired,
} from '../notification/domain-notifications.js'
import { retryCloudtentaclesPendingReturns } from './cloudtentacles-number-recycle-service.js'

type CloudtentaclesHealthAccountResult = {
  sourceKey: string
  label: string
  enabled: boolean
  hasToken: boolean
  ok: boolean
  skipped: boolean
  status: string
  message: string
  asset: number | null
  threshold: number
  checkedAt: string
}

export async function runCloudtentaclesHealthJob(job: JsonObject) {
  const accounts = resolveCloudtentaclesHealthAccounts(job)
  const cooldownSeconds = Number(job.cooldownSeconds || 1800)
  const [results, returnRetryResult] = await Promise.all([
    Promise.all(
      accounts.map((account) => runCloudtentaclesHealthAccount(account, cooldownSeconds)),
    ),
    retryCloudtentaclesPendingReturns(),
  ])
  const checkedResults = results.filter((item) => !item.skipped)
  const failedResults = checkedResults.filter((item) => item.ok === false)
  const lowAssetResults = checkedResults.filter((item) => item.status === 'asset_low')
  const okResults = checkedResults.filter((item) => item.status === 'ok')
  const assetValues = checkedResults
    .map((item) => item.asset)
    .filter((value): value is number => typeof value === 'number' && Number.isFinite(value))
  const summary = buildCloudtentaclesHealthSummary(results)
  const status = resolveCloudtentaclesHealthStatus(results)
  const orphanTaskCount = Number(returnRetryResult?.orphanCount || 0)
  const recycleNote =
    orphanTaskCount > 0
      ? `，另有 ${orphanTaskCount} 个孤儿号码待人工/上游清理`
      : returnRetryResult?.returned
        ? '，发货后退号重试正常'
        : ''
  const finalMessage = `${summary}${recycleNote}`

  return {
    ok: failedResults.length === 0,
    status,
    message: finalMessage,
    accountCount: results.length,
    checkedCount: checkedResults.length,
    okCount: okResults.length,
    lowAssetCount: lowAssetResults.length,
    failedCount: failedResults.length,
    asset: assetValues.length > 0 ? Math.min(...assetValues) : null,
    threshold:
      checkedResults.length > 0
        ? Math.max(...checkedResults.map((item) => Number(item.threshold || 0)))
        : normalizeNonNegativeInteger(job.config?.assetThreshold, 500),
    accounts: results,
    pendingReturnRetry: returnRetryResult,
  }
}

async function runCloudtentaclesHealthAccount(
  account: JsonObject,
  cooldownSeconds: number,
): Promise<CloudtentaclesHealthAccountResult> {
  const sourceKey = String(account.sourceKey || '').trim() || 'default'
  const source = getCloudtentaclesSourceByKey(sourceKey)
  const session: JsonObject = getCloudtentaclesSessionStateByKey(sourceKey) || {}
  const label =
    String(account.label || source?.label || source?.username || sourceKey).trim() || sourceKey
  const threshold = normalizeNonNegativeInteger(account.assetThreshold, 500)
  const checkedAt = new Date().toISOString()

  if (!source) {
    return {
      sourceKey,
      label,
      enabled: account.enabled !== false,
      hasToken: false,
      ok: false,
      skipped: false,
      status: 'source_missing',
      message: `账号 ${label} 不存在`,
      asset: null,
      threshold,
      checkedAt,
    }
  }

  if (account.enabled === false || source.enabled === false) {
    return {
      sourceKey,
      label,
      enabled: false,
      hasToken: Boolean(String(session.token || '').trim()),
      ok: true,
      skipped: true,
      status: 'disabled',
      message: `账号 ${label} 已停用`,
      asset: null,
      threshold,
      checkedAt,
    }
  }

  const token = String(session.token || '').trim()

  if (!token) {
    await notifyCloudtentaclesAuthExpired({
      pathname: '/user/get_asset',
      errorCode: 'cloudtentacles_token_missing',
      message: `账号 ${label} 没有可用 token，请到后台重新登录`,
      cooldownSeconds,
      sourceKey,
      accountLabel: label,
    })
    return {
      ok: false,
      status: 'auth_missing',
      message: `账号 ${label} token 缺失`,
      asset: null,
      threshold,
      sourceKey,
      label,
      enabled: true,
      hasToken: false,
      skipped: false,
      checkedAt,
    }
  }

  try {
    const assetResult = await getCloudtentaclesAsset({
      sourceKey,
      accountLabel: label,
      baseUrl: String(session.baseUrl || source.baseUrl || '').trim() || 'https://123.207.217.176',
      token,
      deviceId: normalizeCloudtentaclesDeviceId(session.deviceId || source.deviceId),
      deviceType: normalizeCloudtentaclesDeviceType(session.deviceType ?? source.deviceType),
    })
    const asset = Number(assetResult.asset || 0)

    if (asset < threshold) {
      await notifyCloudtentaclesAssetLow({
        asset,
        threshold,
        cooldownSeconds,
        sourceKey,
        accountLabel: label,
      })
    }

    return {
      ok: true,
      status: asset < threshold ? 'asset_low' : 'ok',
      message:
        asset < threshold
          ? `账号 ${label} 余额 ${asset} 低于阈值 ${threshold}`
          : `账号 ${label} 余额 ${asset} 正常`,
      asset,
      threshold,
      sourceKey,
      label,
      enabled: true,
      hasToken: true,
      skipped: false,
      checkedAt,
    }
  } catch (error) {
    const message = error instanceof Error ? error.message : String(error || '余额检查失败')
    return {
      ok: false,
      status: 'failed',
      message: `账号 ${label} 检查失败：${message}`,
      asset: null,
      threshold,
      sourceKey,
      label,
      enabled: true,
      hasToken: true,
      skipped: false,
      checkedAt,
    }
  }
}

function resolveCloudtentaclesHealthAccounts(job: JsonObject) {
  const config = isPlainObject(job.config) ? job.config : {}
  const rawAccounts = Array.isArray(config.accounts) ? config.accounts : []
  const defaultThreshold = normalizeNonNegativeInteger(config.assetThreshold, 500)
  const sourceConfig = listCloudtentaclesSources()
  const sources = Array.isArray(sourceConfig.sources) ? sourceConfig.sources : []
  const sourceMap = new Map(
    sources
      .map((source) => [String(source.key || '').trim(), source] as const)
      .filter(([sourceKey]) => Boolean(sourceKey)),
  )
  const configuredMap = new Map(
    rawAccounts
      .map((item) => normalizeCloudtentaclesHealthAccount(item, defaultThreshold, sourceMap))
      .filter(Boolean)
      .map((item) => [String(item!.sourceKey || '').trim(), item!] as const)
      .filter(([sourceKey]) => Boolean(sourceKey)),
  )

  // 以平台配置中的全部账号为准，合并任务里已保存的阈值/开关；新增账号自动纳入监控。
  const mergedAccounts = sources
    .map((source) => {
      const sourceKey = String(source.key || '').trim()
      if (!sourceKey) {
        return null
      }

      const configured = configuredMap.get(sourceKey)
      return {
        sourceKey,
        label:
          String(configured?.label || source.label || source.username || sourceKey).trim() ||
          sourceKey,
        enabled: configured ? configured.enabled !== false : source.enabled !== false,
        assetThreshold: normalizeNonNegativeInteger(configured?.assetThreshold, defaultThreshold),
      }
    })
    .filter(Boolean) as JsonObject[]

  // 保留已配置但源账号已删除的项，便于报 source_missing。
  for (const [sourceKey, configured] of configuredMap.entries()) {
    if (sourceMap.has(sourceKey)) {
      continue
    }
    mergedAccounts.push(configured)
  }

  if (mergedAccounts.length > 0) {
    return dedupeAccounts(mergedAccounts)
  }

  // 兼容旧配置：没有任何平台账号时，仍检查历史 default。
  const fallback = configuredMap.get('default')
  return [
    {
      sourceKey: 'default',
      label: String(fallback?.label || '默认账号').trim() || '默认账号',
      enabled: fallback ? fallback.enabled !== false : true,
      assetThreshold: normalizeNonNegativeInteger(fallback?.assetThreshold, defaultThreshold),
    },
  ]
}

function normalizeCloudtentaclesHealthAccount(
  rawValue: unknown,
  defaultThreshold: number,
  sourceMap: Map<string, JsonObject>,
) {
  const source = isPlainObject(rawValue) ? rawValue : {}
  const sourceKey = String(source.sourceKey || source.key || '').trim()
  if (!sourceKey) {
    return null
  }

  const sourceConfig = sourceMap.get(sourceKey) || {}

  return {
    sourceKey,
    label: String(source.label || sourceConfig.label || sourceConfig.username || sourceKey).trim(),
    enabled: source.enabled !== false,
    assetThreshold: normalizeNonNegativeInteger(
      source.assetThreshold ?? source.threshold,
      defaultThreshold,
    ),
  }
}

function dedupeAccounts(accounts: JsonObject[]) {
  const seen = new Set<string>()
  return accounts.filter((item) => {
    const sourceKey = String(item.sourceKey || '').trim()
    if (!sourceKey || seen.has(sourceKey)) {
      return false
    }
    seen.add(sourceKey)
    return true
  })
}

function buildCloudtentaclesHealthSummary(results: CloudtentaclesHealthAccountResult[]) {
  if (results.length === 0) {
    return '未配置 kuaishou-lewan 监控账号'
  }

  const checkedResults = results.filter((item) => !item.skipped)
  const lowAssetResults = checkedResults.filter((item) => item.status === 'asset_low')
  const failedResults = checkedResults.filter((item) => item.ok === false)
  const disabledCount = results.length - checkedResults.length

  if (checkedResults.length === 0) {
    return `所有账号均已停用，共 ${disabledCount} 个`
  }

  if (failedResults.length > 0 || lowAssetResults.length > 0) {
    return [
      `检查 ${checkedResults.length} 个账号`,
      lowAssetResults.length > 0 ? `余额预警 ${lowAssetResults.length} 个` : '',
      failedResults.length > 0 ? `异常 ${failedResults.length} 个` : '',
      disabledCount > 0 ? `停用 ${disabledCount} 个` : '',
    ]
      .filter(Boolean)
      .join('，')
  }

  return `检查 ${checkedResults.length} 个账号，余额均正常${disabledCount > 0 ? `，停用 ${disabledCount} 个` : ''}`
}

function resolveCloudtentaclesHealthStatus(results: CloudtentaclesHealthAccountResult[]) {
  const checkedResults = results.filter((item) => !item.skipped)
  if (checkedResults.length === 0) {
    return 'skipped'
  }

  if (
    checkedResults.some((item) =>
      ['auth_missing', 'source_missing', 'failed'].includes(item.status),
    )
  ) {
    return 'failed'
  }

  if (checkedResults.some((item) => item.status === 'asset_low')) {
    return 'asset_low'
  }

  return 'ok'
}

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

function isPlainObject(value: unknown): value is JsonObject {
  return Boolean(value) && typeof value === 'object' && !Array.isArray(value)
}
