import type { JsonObject } from '../../../../types/json.js'
import {
  getCloudtentaclesSourceByKey,
  getCloudtentaclesSourcesFilePath,
  listCloudtentaclesSources,
  saveCloudtentaclesSourceByKey,
  saveCloudtentaclesSourcesList,
  deleteCloudtentaclesSourceByKey,
} from '../../../platforms/cloudtentacles/source-config-service.js'
import {
  clearCloudtentaclesSessionStateByKey,
  deleteCloudtentaclesSessionStateByKey,
  getCloudtentaclesSessionFilePath,
  getCloudtentaclesSessionStateByKey,
  saveCloudtentaclesSessionStateByKey,
  getAllCloudtentaclesSessionStates,
  pruneCloudtentaclesSessionStates,
} from '../../../platforms/cloudtentacles/session-state-service.js'
import {
  loginCloudtentaclesSession,
  sendCloudtentaclesSmsCode,
  validateCloudtentaclesSession,
} from '../../../platforms/cloudtentacles/session-service.js'
import {
  buyCloudtentaclesSku,
  getCloudtentaclesAsset,
  getCloudtentaclesCategories,
  listCloudtentaclesSku,
  useCloudtentaclesSku,
} from '../../../platforms/cloudtentacles/catalog-service.js'
import { getCloudtentaclesKnapsack } from '../../../platforms/cloudtentacles/knapsack-service.js'
import { listCloudtentaclesDeliveryRecords } from '../../../platforms/cloudtentacles/record-service.js'
import {
  appointCloudtentaclesVirtualNumber,
  backCloudtentaclesVirtualNumber,
  fetchCloudtentaclesVirtualNumberCode,
  generateCloudtentaclesLoginCode,
  getCloudtentaclesBindUrl,
  listCloudtentaclesVirtualNumbers,
  verifyCloudtentaclesLoginCode,
} from '../../../platforms/cloudtentacles/virtual-number-service.js'
import { runCloudtentaclesFullDebugFlow } from '../../../platforms/cloudtentacles/debug-flow-service.js'
import {
  getCloudtentaclesOverrideRuleConfig,
  getCloudtentaclesOverrideRuleFilePath,
  saveCloudtentaclesOverrideRuleConfig,
} from '../../../order/cloudtentacles-override-rule-service.js'
import { hasCloudtentaclesCredentialContextChanged, pickFirstNonEmpty } from './context.js'
import {
  buildAdminCloudtentaclesLoginResult,
  buildAdminCloudtentaclesPersistedSessionPayload,
  buildAdminCloudtentaclesValidateResult,
  resolveAdminCloudtentaclesCredentialPayload,
  resolveAdminCloudtentaclesSessionPayload,
} from './payloads.js'
import { normalizeAdminCloudtentaclesSourceConfigPayload } from './writes.js'
import {
  mapAdminCloudtentaclesSession,
  mapAdminCloudtentaclesSourceConfig,
  maskPhone,
} from './mappers.js'
import { createHttpError } from '../../../../utils/http.js'
import { query as dbQuery } from '../../../../db/client.js'
import { parseJsonObject } from '../../../../utils/task-json.js'

import type {
  AdminCloudtentaclesCatalogQueryInput,
  AdminCloudtentaclesDeliveryRecordQueryInput,
  AdminCloudtentaclesFullFlowInput,
  AdminCloudtentaclesOverrideRuleConfigInput,
  AdminCloudtentaclesSendSmsCodeInput,
  AdminCloudtentaclesSkuBuyInput,
  AdminCloudtentaclesSkuUseInput,
  AdminCloudtentaclesSourceConfigInput,
  AdminCloudtentaclesTestLoginInput,
  AdminCloudtentaclesValidateSessionInput,
  AdminCloudtentaclesVirtualNumberInput,
} from '../../../../types/admin/write-inputs.js'

type LocalCloudtentaclesTask = {
  taskId: number
  taskNo: string
  platformOrderId: string
  orderShopId: string
  orderShopName: string
  taskStatus: string
  deliveryStatus: string
  resultCode: string
  resultMessage: string
  createdAt: string
  updatedAt: string
  skuCode: string
  skuName: string
  sourceKey: string
  vnId: number
  vnPhone: string
  roleName: string
  roleId: string
  localDispatchStatus: string
  localDispatchAt: string
  consumeShopId: string
  consumeShopName: string
}

type LocalCloudtentaclesTaskRow = {
  id: number
  task_no: string
  platform_order_id: string
  shop_id: string
  shop_name: string
  task_status: string
  delivery_status: string
  result_code: string
  result_message: string
  context_json: unknown
  created_at: string
  updated_at: string
  sku_code?: string
  sku_name?: string
  kuaishou_source_key?: string
  kuaishou_vn_phone?: string
  kuaishou_role_name?: string
  kuaishou_role_id?: string
  kuaishou_dispatch_status?: string
  kuaishou_consume_status?: string
}

export function listAdminCloudtentaclesSources() {
  const list = listCloudtentaclesSources()
  const sessions = getAllCloudtentaclesSessionStates()

  const sources = Array.isArray(list.sources)
    ? list.sources.map((s) => mapAdminCloudtentaclesSourceConfig(s))
    : []

  const mappedSessions: JsonObject = {}
  for (const [key, session] of Object.entries(sessions.sessions || {})) {
    mappedSessions[key] = mapAdminCloudtentaclesSession(session)
  }

  return {
    filePath: getCloudtentaclesSourcesFilePath(),
    sessionFilePath: getCloudtentaclesSessionFilePath(),
    enabled: list.enabled,
    sources,
    sessions: mappedSessions,
  }
}

export function listAdminCloudtentaclesRecordSources() {
  const list = listCloudtentaclesSources()
  const sessions = getAllCloudtentaclesSessionStates()
  const sources = Array.isArray(list.sources) ? list.sources : []

  return {
    sources: sources.map((source) => {
      const key = String(source.key || '').trim()
      const session = (sessions.sessions?.[key] || {}) as JsonObject
      return {
        key,
        label: String(source.label || source.username || key || '未命名账号').trim(),
        enabled: source.enabled !== false,
        hasToken: Boolean(String(session.token || '').trim()),
        loggedInAt: String(session.loggedInAt || '').trim(),
      }
    }),
  }
}

export function getAdminCloudtentaclesOverrideRules() {
  return {
    filePath: getCloudtentaclesOverrideRuleFilePath(),
    ...getCloudtentaclesOverrideRuleConfig(),
  }
}

export async function updateAdminCloudtentaclesOverrideRules(
  payload: AdminCloudtentaclesOverrideRuleConfigInput = {},
) {
  return {
    filePath: getCloudtentaclesOverrideRuleFilePath(),
    ...(await saveCloudtentaclesOverrideRuleConfig(payload)),
  }
}

export async function updateAdminCloudtentaclesSourceConfig(
  payload: AdminCloudtentaclesSourceConfigInput = {},
) {
  // 批量保存：payload.sources 是数组时替换整份账号列表。
  if (Array.isArray(payload.sources)) {
    const normalizedList = payload.sources.map((item) =>
      normalizeAdminCloudtentaclesSourceConfigPayload(item),
    )
    const savedList = await saveCloudtentaclesSourcesList({
      enabled: payload.enabled !== false,
      sources: normalizedList,
    })
    const sessions = await pruneCloudtentaclesSessionStates(normalizedList.map((item) => item.key))

    const sources = Array.isArray(savedList.sources)
      ? savedList.sources.map((s) => mapAdminCloudtentaclesSourceConfig(s))
      : []
    const mappedSessions: JsonObject = {}
    for (const [key, session] of Object.entries(sessions.sessions || {})) {
      mappedSessions[key] = mapAdminCloudtentaclesSession(session)
    }

    return {
      filePath: getCloudtentaclesSourcesFilePath(),
      sessionFilePath: getCloudtentaclesSessionFilePath(),
      enabled: savedList.enabled,
      sources,
      sessions: mappedSessions,
    }
  }

  // 单账号保存。
  const sourceKey = String(payload.sourceKey || '').trim() || 'default'
  const current = getCloudtentaclesSourceByKey(sourceKey)
  const normalized = normalizeAdminCloudtentaclesSourceConfigPayload({
    ...payload,
    sourceKey,
  })
  const saved = await saveCloudtentaclesSourceByKey(sourceKey, normalized)
  const shouldClearSession = hasCloudtentaclesCredentialContextChanged(current || undefined, saved)
  const session = shouldClearSession
    ? (await clearCloudtentaclesSessionStateByKey(sourceKey),
      getCloudtentaclesSessionStateByKey(sourceKey))
    : getCloudtentaclesSessionStateByKey(sourceKey)

  return {
    filePath: getCloudtentaclesSourcesFilePath(),
    sessionFilePath: getCloudtentaclesSessionFilePath(),
    source: mapAdminCloudtentaclesSourceConfig(saved),
    session: mapAdminCloudtentaclesSession(session || undefined),
  }
}

export async function deleteAdminCloudtentaclesSource(sourceKey: string) {
  const key = String(sourceKey || '').trim()
  if (!key) {
    throw createHttpError('cloudtentacles sourceKey 不能为空', {
      statusCode: 400,
      errorCode: 'cloudtentacles_source_key_required',
    })
  }
  await deleteCloudtentaclesSessionStateByKey(key)
  await deleteCloudtentaclesSourceByKey(key)
}

export async function sendAdminCloudtentaclesSmsCode(
  payload: AdminCloudtentaclesSendSmsCodeInput = {},
) {
  const result = await sendCloudtentaclesSmsCode(
    resolveAdminCloudtentaclesCredentialPayload(payload),
  )

  return {
    ...result,
    phoneMasked: maskPhone(result.phone),
  }
}

export async function testAdminCloudtentaclesLogin(
  payload: AdminCloudtentaclesTestLoginInput = {},
) {
  const sourceKey = String(payload.sourceKey || '').trim() || 'default'
  const savedSource = getCloudtentaclesSourceByKey(sourceKey) || {}
  const credentialContext = resolveAdminCloudtentaclesCredentialPayload(payload, { savedSource })
  const session = await loginCloudtentaclesSession({
    ...credentialContext,
    code: String(payload.code || '').trim(),
  })
  const savedSession = await saveCloudtentaclesSessionStateByKey(
    sourceKey,
    buildAdminCloudtentaclesPersistedSessionPayload(session, {
      deviceId: credentialContext.deviceId,
      deviceType: credentialContext.deviceType,
    }),
  )

  return buildAdminCloudtentaclesLoginResult(session, savedSession, sourceKey)
}

export async function validateAdminCloudtentaclesSession(
  payload: AdminCloudtentaclesValidateSessionInput = {},
) {
  const sourceKey = String(payload.sourceKey || '').trim() || 'default'
  const savedSource: JsonObject = getCloudtentaclesSourceByKey(sourceKey) || {}
  const persistedSession: JsonObject = getCloudtentaclesSessionStateByKey(sourceKey) || {}
  const sessionContext = resolveAdminCloudtentaclesSessionPayload(payload, {
    savedSource,
    persistedSession,
  })
  const session = await validateCloudtentaclesSession(sessionContext)
  const savedSession = await saveCloudtentaclesSessionStateByKey(
    sourceKey,
    buildAdminCloudtentaclesPersistedSessionPayload(session, {
      username: pickFirstNonEmpty([persistedSession.username, savedSource.username]),
      phone: pickFirstNonEmpty([persistedSession.phone, savedSource.phone]),
      deviceId: sessionContext.deviceId,
      deviceType: sessionContext.deviceType,
    }),
  )

  return buildAdminCloudtentaclesValidateResult(session, savedSession, sourceKey)
}

export async function getAdminCloudtentaclesAsset(
  payload: AdminCloudtentaclesCatalogQueryInput = {},
) {
  return getCloudtentaclesAsset(resolveAdminCloudtentaclesSessionPayload(payload))
}

export async function getAdminCloudtentaclesCategories(
  payload: AdminCloudtentaclesCatalogQueryInput = {},
) {
  return getCloudtentaclesCategories(resolveAdminCloudtentaclesSessionPayload(payload))
}

export async function getAdminCloudtentaclesSkuList(
  payload: AdminCloudtentaclesCatalogQueryInput = {},
) {
  return listCloudtentaclesSku(resolveAdminCloudtentaclesSessionPayload(payload))
}

export async function buyAdminCloudtentaclesSku(payload: AdminCloudtentaclesSkuBuyInput = {}) {
  const context = resolveAdminCloudtentaclesSessionPayload(payload)
  return buyCloudtentaclesSku({
    ...context,
    id: payload.id,
    count: payload.count,
  })
}

export async function useAdminCloudtentaclesSku(payload: AdminCloudtentaclesSkuUseInput = {}) {
  const context = resolveAdminCloudtentaclesSessionPayload(payload)
  return useCloudtentaclesSku({
    ...context,
    id: payload.id,
    virtualNumberId: payload.virtualNumberId,
    phone: payload.phone,
  })
}

export async function getAdminCloudtentaclesKnapsack(
  payload: AdminCloudtentaclesCatalogQueryInput = {},
) {
  return getCloudtentaclesKnapsack(resolveAdminCloudtentaclesSessionPayload(payload))
}

export async function listAdminCloudtentaclesDeliveryRecords(
  payload: AdminCloudtentaclesDeliveryRecordQueryInput = {},
) {
  const platformOrderId = normalizeText(payload.platformOrderId)
  const preferredSourceKey = normalizeText(payload.sourceKey)
  const orderMatchedTasks = platformOrderId
    ? await listLocalCloudtentaclesTasks({
        platformOrderId,
        limit: 50,
      })
    : []
  const orderSourceKey =
    orderMatchedTasks.find((task) => !preferredSourceKey || task.sourceKey === preferredSourceKey)
      ?.sourceKey || orderMatchedTasks.find((task) => task.sourceKey)?.sourceKey
  const resolvedSourceKey = orderSourceKey || preferredSourceKey || 'default'
  const context = resolveAdminCloudtentaclesSessionPayload({
    ...payload,
    sourceKey: resolvedSourceKey,
  })
  const recordWindow = resolveRecordQueryWindow(payload, orderMatchedTasks)
  const recordPage = platformOrderId ? 1 : payload.page
  const recordSize = platformOrderId ? 1000 : payload.size
  const query = {
    ...context,
    ...pickDefined({
      page: recordPage,
      size: recordSize,
      startDate: recordWindow.startDate,
      endDate: recordWindow.endDate,
      recordCode: payload.recordCode,
    }),
  }
  const [records, skuList] = await Promise.all([
    listCloudtentaclesDeliveryRecords(query),
    listCloudtentaclesSku(context),
  ])
  const skuImageByName = new Map<
    string,
    {
      skuId: number
      skuName: string
      skuImage: string
    }
  >()
  for (const sku of Array.isArray(skuList.items) ? skuList.items : []) {
    const name = normalizeSkuName(sku.name)
    const skuImage = String(sku.image || '').trim()
    if (!name || !skuImage) {
      continue
    }

    skuImageByName.set(name, {
      skuId: Number(sku.id || 0),
      skuName: String(sku.name || '').trim(),
      skuImage,
    })
  }

  const recordItems: JsonObject[] = Array.isArray(records.items) ? records.items : []
  const localTasks =
    platformOrderId || recordItems.length === 0
      ? orderMatchedTasks
      : await listLocalCloudtentaclesTasks({
          sourceKey: resolvedSourceKey,
          startDate: recordWindow.startDate,
          endDate: recordWindow.endDate,
          limit: 800,
        })
  const enrichedItems = recordItems
    .map((record) => {
      const localMatch = platformOrderId
        ? findStrictLocalTaskForOrderRecord(record, localTasks)
        : findBestLocalTaskForRecord(record, localTasks)
      return {
        ...record,
        ...(skuImageByName.get(normalizeSkuName(record.name)) || {
          skuId: 0,
          skuName: '',
          skuImage: '',
        }),
        ...mapLocalTaskRecordEnhancement(localMatch?.task || null, localMatch?.confidence || ''),
      }
    })
    .filter((record) => {
      if (!platformOrderId) {
        return true
      }
      return normalizeText(record.platformOrderId) === platformOrderId
    })
  const fallbackItems = platformOrderId
    ? orderMatchedTasks
        .filter(
          (task) => !enrichedItems.some((record) => Number(record.taskId || 0) === task.taskId),
        )
        .map((task) => ({
          ...createLocalTaskFallbackRecord(task),
          ...(skuImageByName.get(normalizeSkuName(task.skuName)) || {
            skuId: 0,
            skuName: '',
            skuImage: '',
          }),
          ...mapLocalTaskRecordEnhancement(task, 'local'),
        }))
    : []
  const finalItems = platformOrderId ? [...enrichedItems, ...fallbackItems] : enrichedItems

  return {
    ...records,
    sourceKey: resolvedSourceKey,
    queryStartDate: recordWindow.startDate,
    queryEndDate: recordWindow.endDate,
    total: platformOrderId ? finalItems.length : records.total,
    page: platformOrderId ? 1 : records.page,
    size: platformOrderId ? recordSize : records.size,
    localOrderMatched: platformOrderId ? orderMatchedTasks.length > 0 : false,
    items: finalItems,
  }
}

export async function listAdminCloudtentaclesVirtualNumbers(
  payload: AdminCloudtentaclesVirtualNumberInput = {},
) {
  const context = resolveAdminCloudtentaclesSessionPayload(payload)
  return listCloudtentaclesVirtualNumbers({
    ...context,
    key: payload.key,
  })
}

export async function appointAdminCloudtentaclesVirtualNumber(
  payload: AdminCloudtentaclesVirtualNumberInput = {},
) {
  const context = resolveAdminCloudtentaclesSessionPayload(payload)
  return appointCloudtentaclesVirtualNumber({
    ...context,
    key: payload.key,
  })
}

export async function generateAdminCloudtentaclesLoginCode(
  payload: AdminCloudtentaclesVirtualNumberInput = {},
) {
  const context = resolveAdminCloudtentaclesSessionPayload(payload)
  return generateCloudtentaclesLoginCode({
    ...context,
    key: payload.key,
    id: payload.id,
  })
}

export async function fetchAdminCloudtentaclesVirtualNumberCode(
  payload: AdminCloudtentaclesVirtualNumberInput = {},
) {
  const context = resolveAdminCloudtentaclesSessionPayload(payload)
  return fetchCloudtentaclesVirtualNumberCode({
    ...context,
    key: payload.key,
    phone: payload.phone,
  })
}

export async function verifyAdminCloudtentaclesLoginCode(
  payload: AdminCloudtentaclesVirtualNumberInput = {},
) {
  const context = resolveAdminCloudtentaclesSessionPayload(payload)
  return verifyCloudtentaclesLoginCode({
    ...context,
    key: payload.key,
    id: payload.id,
    code: payload.code,
  })
}

export async function getAdminCloudtentaclesBindUrl(
  payload: AdminCloudtentaclesVirtualNumberInput = {},
) {
  const context = resolveAdminCloudtentaclesSessionPayload(payload)
  return getCloudtentaclesBindUrl({
    ...context,
    key: payload.key,
    id: payload.id,
  })
}

export async function backAdminCloudtentaclesVirtualNumber(
  payload: AdminCloudtentaclesVirtualNumberInput = {},
) {
  const context = resolveAdminCloudtentaclesSessionPayload(payload)
  return backCloudtentaclesVirtualNumber({
    ...context,
    key: payload.key,
    id: payload.id,
  })
}

export async function runAdminCloudtentaclesFullFlow(
  payload: AdminCloudtentaclesFullFlowInput = {},
) {
  const context = resolveAdminCloudtentaclesSessionPayload(payload)
  return runCloudtentaclesFullDebugFlow({
    ...context,
    skuId: payload.skuId,
    skuCount: payload.skuCount,
    vnKey: payload.vnKey,
  })
}

function pickDefined(values: JsonObject = {}) {
  return Object.fromEntries(
    Object.entries(values).filter(([, value]) => typeof value !== 'undefined'),
  )
}

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

function normalizeText(value: unknown) {
  return String(value || '').trim()
}

function normalizePhone(value: unknown) {
  return normalizeText(value).replace(/\D+/g, '')
}

function normalizeNumber(value: unknown) {
  const parsed = Number(value)
  return Number.isFinite(parsed) && parsed > 0 ? parsed : 0
}

function asRecord(value: unknown): JsonObject {
  return value && typeof value === 'object' && !Array.isArray(value) ? (value as JsonObject) : {}
}

function pickFirstNonEmptyText(values: unknown[]) {
  for (const value of values) {
    const normalized = normalizeText(value)
    if (normalized) {
      return normalized
    }
  }

  return ''
}

function resolveRecordQueryWindow(
  payload: AdminCloudtentaclesDeliveryRecordQueryInput = {},
  tasks: LocalCloudtentaclesTask[] = [],
) {
  if (tasks.length === 0) {
    return {
      startDate: payload.startDate,
      endDate: payload.endDate,
    }
  }

  const timestamps = tasks
    .flatMap((task) => [task.createdAt, task.updatedAt, task.localDispatchAt])
    .map((value) => new Date(value).getTime())
    .filter((value) => Number.isFinite(value))
  if (timestamps.length === 0) {
    return {
      startDate: payload.startDate,
      endDate: payload.endDate,
    }
  }

  const hourMs = 60 * 60 * 1000
  return {
    startDate: new Date(Math.min(...timestamps) - hourMs).toISOString(),
    endDate: new Date(Math.max(...timestamps) + 6 * hourMs).toISOString(),
  }
}

async function listLocalCloudtentaclesTasks(
  options: {
    platformOrderId?: string | undefined
    sourceKey?: string | undefined
    startDate?: string | undefined
    endDate?: string | undefined
    limit?: number
  } = {},
): Promise<LocalCloudtentaclesTask[]> {
  const filters: string[] = ["ft.executor_key = 'kuaishou_ct_assisted'"]
  const params: unknown[] = []
  const platformOrderId = normalizeText(options.platformOrderId)
  const sourceKey = normalizeText(options.sourceKey)
  const startDate = normalizeText(options.startDate)
  const endDate = normalizeText(options.endDate)

  if (platformOrderId) {
    params.push(`%${platformOrderId}%`)
    filters.push(`ft.platform_order_id ILIKE $${params.length}`)
  }

  if (sourceKey) {
    params.push(sourceKey)
    filters.push(
      `COALESCE(NULLIF(kcts.source_key, ''), ft.context_json #>> '{kuaishouCloudFulfillment,binding,resolvedSourceKey}', '') = $${params.length}`,
    )
  }

  if (startDate) {
    params.push(startDate)
    filters.push(`ft.updated_at >= ($${params.length})::timestamptz - interval '1 day'`)
  }

  if (endDate) {
    params.push(endDate)
    filters.push(`ft.created_at <= ($${params.length})::timestamptz + interval '1 day'`)
  }

  params.push(Math.min(Math.max(Number(options.limit || 500), 1), 1000))
  const result = await dbQuery<LocalCloudtentaclesTaskRow>(
    `
      SELECT
        ft.id,
        ft.task_no,
        ft.platform_order_id,
        ft.shop_id,
        ft.shop_name,
        ft.task_status,
        ft.delivery_status,
        ft.result_code,
        ft.result_message,
        ft.context_json,
        ft.created_at,
        ft.updated_at,
        oi.sku_code,
        oi.sku_name,
        kcts.source_key AS kuaishou_source_key,
        kcts.vn_phone AS kuaishou_vn_phone,
        kcts.role_name AS kuaishou_role_name,
        kcts.role_id AS kuaishou_role_id,
        kcts.dispatch_status AS kuaishou_dispatch_status,
        kcts.consume_status AS kuaishou_consume_status
      FROM fulfillment_tasks ft
      LEFT JOIN order_items oi ON oi.id = ft.order_item_id
      LEFT JOIN kuaishou_cloud_task_states kcts ON kcts.task_id = ft.id
      WHERE ${filters.join(' AND ')}
      ORDER BY ft.id DESC
      LIMIT $${params.length}
    `,
    params,
  )

  return result.rows.map(mapLocalCloudtentaclesTask)
}

function mapLocalCloudtentaclesTask(row: LocalCloudtentaclesTaskRow): LocalCloudtentaclesTask {
  const context = parseJsonObject(row.context_json)
  const flow = asRecord(context.kuaishouCloudFulfillment)
  const binding = asRecord(flow.binding)
  const role = asRecord(flow.role)
  const dispatch = asRecord(flow.dispatch)
  const consume = asRecord(flow.consume)

  return {
    taskId: Number(row.id || 0),
    taskNo: normalizeText(row.task_no),
    platformOrderId: normalizeText(row.platform_order_id),
    orderShopId: normalizeText(row.shop_id),
    orderShopName: normalizeText(row.shop_name),
    taskStatus: normalizeText(row.task_status),
    deliveryStatus: normalizeText(row.delivery_status),
    resultCode: normalizeText(row.result_code),
    resultMessage: normalizeText(row.result_message),
    createdAt: normalizeText(row.created_at),
    updatedAt: normalizeText(row.updated_at),
    skuCode: normalizeText(row.sku_code),
    skuName: pickFirstNonEmptyText([row.sku_name, flow.internalSkuName]),
    sourceKey: pickFirstNonEmptyText([row.kuaishou_source_key, binding.resolvedSourceKey]),
    vnId: normalizeNumber(binding.vnId),
    vnPhone: pickFirstNonEmptyText([row.kuaishou_vn_phone, binding.vnPhone]),
    roleName: pickFirstNonEmptyText([row.kuaishou_role_name, role.name, binding.roleName]),
    roleId: pickFirstNonEmptyText([row.kuaishou_role_id, role.rid, binding.roleId]),
    localDispatchStatus: pickFirstNonEmptyText([row.kuaishou_dispatch_status, dispatch.status]),
    localDispatchAt: normalizeText(dispatch.dispatchAt),
    consumeShopId: normalizeText(consume.shopId),
    consumeShopName: normalizeText(consume.shopName),
  }
}

function findBestLocalTaskForRecord(record: JsonObject, tasks: LocalCloudtentaclesTask[] = []) {
  let best: { task: LocalCloudtentaclesTask; score: number; confidence: string } | null = null

  for (const task of tasks) {
    const score = scoreLocalTaskRecordMatch(record, task)
    if (score <= 0 || (best && score <= best.score)) {
      continue
    }

    best = {
      task,
      score,
      confidence: score >= 80 ? 'high' : score >= 45 ? 'medium' : 'low',
    }
  }

  return best && best.score >= 35 ? best : null
}

function findStrictLocalTaskForOrderRecord(
  record: JsonObject,
  tasks: LocalCloudtentaclesTask[] = [],
) {
  let best: { task: LocalCloudtentaclesTask; score: number; confidence: string } | null = null

  for (const task of tasks) {
    if (!isSameDispatchedResource(record, task)) {
      continue
    }

    const score = scoreLocalTaskRecordMatch(record, task)
    if (score <= 0 || (best && score <= best.score)) {
      continue
    }

    best = {
      task,
      score,
      confidence: score >= 80 ? 'high' : 'medium',
    }
  }

  return best
}

function isSameDispatchedResource(record: JsonObject, task: LocalCloudtentaclesTask) {
  const recordVnId = normalizeNumber(record.virtualNumberId)
  const recordPhone = normalizePhone(record.phone)
  const taskPhone = normalizePhone(task.vnPhone)

  if (recordVnId > 0 && task.vnId > 0 && recordVnId === task.vnId) {
    return true
  }

  return Boolean(recordPhone && taskPhone && recordPhone === taskPhone)
}

function scoreLocalTaskRecordMatch(record: JsonObject, task: LocalCloudtentaclesTask) {
  let score = 0
  const recordVnId = normalizeNumber(record.virtualNumberId)
  const recordPhone = normalizePhone(record.phone)
  const taskPhone = normalizePhone(task.vnPhone)
  const recordName = normalizeSkuName(record.name)
  const taskSkuName = normalizeSkuName(task.skuName)
  const recordRoleName = normalizeSkuName(record.fulfillUser)
  const taskRoleName = normalizeSkuName(task.roleName)

  if (recordVnId > 0 && task.vnId > 0 && recordVnId === task.vnId) {
    score += 50
  }
  if (recordPhone && taskPhone && recordPhone === taskPhone) {
    score += 30
  }
  if (recordName && taskSkuName && recordName === taskSkuName) {
    score += 20
  }
  if (recordRoleName && taskRoleName && recordRoleName.includes(taskRoleName)) {
    score += 10
  }

  const recordTime = new Date(record.createdAt).getTime()
  const dispatchTime = new Date(task.localDispatchAt || task.updatedAt || task.createdAt).getTime()
  if (Number.isFinite(recordTime) && Number.isFinite(dispatchTime)) {
    const distanceHours = Math.abs(recordTime - dispatchTime) / (60 * 60 * 1000)
    if (distanceHours <= 6) {
      score += 15
    } else if (distanceHours <= 24) {
      score += 6
    }
  }

  return score
}

function mapLocalTaskRecordEnhancement(task: LocalCloudtentaclesTask | null, confidence = '') {
  return {
    platformOrderId: task?.platformOrderId || '',
    taskId: task?.taskId || 0,
    taskNo: task?.taskNo || '',
    orderShopId: task?.orderShopId || '',
    orderShopName: task?.orderShopName || '',
    consumeShopId: task?.consumeShopId || '',
    consumeShopName: task?.consumeShopName || '',
    roleName: task?.roleName || '',
    roleId: task?.roleId || '',
    localTaskStatus: task?.taskStatus || '',
    localDeliveryStatus: task?.deliveryStatus || '',
    localDispatchStatus: task?.localDispatchStatus || '',
    localDispatchAt: task?.localDispatchAt || '',
    localResultCode: task?.resultCode || '',
    localResultMessage: task?.resultMessage || '',
    matchConfidence: confidence,
  }
}

function createLocalTaskFallbackRecord(task: LocalCloudtentaclesTask) {
  return {
    createdAt: task.localDispatchAt || task.updatedAt || task.createdAt,
    recordId: '',
    virtualNumberId: task.vnId,
    userId: 0,
    count: 1,
    cdk: '',
    exchangeUrl: '',
    name: task.skuName,
    phone: task.vnPhone,
    status: mapLocalDispatchStatusToCloudStatus(task.localDispatchStatus),
    fulfillUser: task.roleName,
    operatorName: '',
    raw: {},
  }
}

function mapLocalDispatchStatusToCloudStatus(status: string) {
  const normalized = normalizeText(status)
  if (normalized === 'success') {
    return 4
  }
  if (normalized === 'failed') {
    return 3
  }
  if (normalized === 'pending') {
    return 2
  }
  return 0
}
