import { safeParseJson } from './admin-query-utils.js'
import { normalizeAdminRole } from './admin-auth-service.js'
import { asJsonObject, type JsonRecord } from '../../types/json.js'
import {
  parseTaskContext as parseTaskContextValue,
  parseTaskState as parseTaskStateValue,
} from '../../utils/task-json.js'
import { canRegenerateClaimLinkStatus, isTaskFinalStatus } from '../../domain/task-status.js'

import type { AdminViewerSessionInput } from '../../types/admin/read-inputs.js'
import type { OrderItemRow, TaskRow } from '../../types/repository/rows.js'

type CloudSourceLabelMap = Map<string, string> | Record<string, string>

type KuaishouCloudFulfillmentMapOptions = {
  cloudSourceLabelMap?: CloudSourceLabelMap
}

type TaskLike = Partial<TaskRow> & {
  state_json?: string | JsonRecord
}

export type AdminViewerContext = {
  role: string
  canViewSensitiveTaskData: boolean
  canManageTaskLifecycle: boolean
  canOperateAssistedTask: boolean
  canOperateKuaishouIndustryVoucher: boolean
}

type RedeemResolutionAttempt = {
  attempt: number
  codeMasked: string
  credentialType: string
  outcome: string
  resultCode: string
  resultMessage: string
}

export function mapManualDispatchContext(
  value: unknown,
  viewerContext: AdminViewerContext = createAdminViewerContext(),
): JsonRecord | null {
  if (!value || typeof value !== 'object') {
    return null
  }

  const record = value as JsonRecord
  return {
    outcome: String(record.outcome || '').trim(),
    deliveryReference: String(record.deliveryReference || '').trim(),
    deliveredCredential: viewerContext.canViewSensitiveTaskData
      ? String(record.deliveredCredential || '').trim()
      : '',
    resultMessage: String(record.resultMessage || '').trim(),
    completedAt: record.completedAt || null,
    completedBy:
      record.completedBy && typeof record.completedBy === 'object'
        ? {
            userId: Number(record.completedBy.userId || 0) || 0,
            username: String(record.completedBy.username || '').trim(),
            role: String(record.completedBy.role || '').trim(),
          }
        : null,
  }
}

export function mapKuaishouCloudFulfillmentContext(
  value: unknown,
  options: KuaishouCloudFulfillmentMapOptions = {},
): JsonRecord | null {
  if (!value || typeof value !== 'object') {
    return null
  }

  const record = value as JsonRecord
  const ticket = asJsonObject(record.ticket)
  const binding = asJsonObject(record.binding)
  const role = asJsonObject(record.role)
  const purchase = asJsonObject(record.purchase)
  const dispatch = asJsonObject(record.dispatch)
  const returnNumber = asJsonObject(record.returnNumber)
  const consume = asJsonObject(record.consume)
  const cloudSourceKeys = Array.isArray(binding.cloudSourceKeys)
    ? binding.cloudSourceKeys.map((value: unknown) => String(value || '').trim()).filter(Boolean)
    : []
  const resolvedSourceKey = String(binding.resolvedSourceKey || '').trim()

  return {
    flowType: String(record.flowType || '').trim(),
    configId: String(record.configId || '').trim(),
    internalSkuCode: String(record.internalSkuCode || '').trim(),
    internalSkuName: String(record.internalSkuName || '').trim(),
    ticket: {
      code: String(ticket.code || '').trim(),
      capturedAt: ticket.capturedAt || null,
      status: String(ticket.status || 'pending').trim() || 'pending',
      verifiedAt: ticket.verifiedAt || null,
      goodsTitle: String(ticket.goodsTitle || '').trim(),
      leftCount: Number(ticket.leftCount || 0) || 0,
    },
    binding: {
      prepareStatus: String(binding.prepareStatus || 'pending').trim() || 'pending',
      cloudSourceKeys,
      cloudSourceLabels: cloudSourceKeys.map((sourceKey: string) =>
        resolveCloudSourceLabel(sourceKey, options.cloudSourceLabelMap),
      ),
      resolvedSourceKey,
      resolvedSourceLabel: resolvedSourceKey
        ? resolveCloudSourceLabel(resolvedSourceKey, options.cloudSourceLabelMap)
        : '',
      skuId: Number(binding.skuId || 0) || 0,
      skuName: String(binding.skuName || '').trim(),
      vnKey: String(binding.vnKey || '').trim(),
      vnId: Number(binding.vnId || 0) || 0,
      vnPhone: String(binding.vnPhone || '').trim(),
      bindUrl: String(binding.bindUrl || '').trim(),
      bindPreparedAt: binding.bindPreparedAt || null,
      bindExpiresAt: binding.bindExpiresAt || null,
      bindProbeAt: binding.bindProbeAt || null,
      bindProbeStatus: String(binding.bindProbeStatus || '').trim(),
      bindProbeMessage: String(binding.bindProbeMessage || '').trim(),
      roleName: String(binding.roleName || role.name || '').trim(),
      roleId: String(binding.roleId || role.rid || '').trim(),
    },
    role: {
      status: String(role.status || 'pending').trim() || 'pending',
      name: String(role.name || binding.roleName || '').trim(),
      rid: String(role.rid || binding.roleId || '').trim(),
      refreshedAt: role.refreshedAt || null,
      errorMessage: String(role.errorMessage || '').trim(),
      rawInfo: role.rawInfo && typeof role.rawInfo === 'object' ? role.rawInfo : null,
    },
    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,
    },
    dispatch: {
      status: String(dispatch.status || 'pending').trim() || 'pending',
      dispatchAt: dispatch.dispatchAt || null,
      sendType: Number(dispatch.sendType || 0) || 0,
      note: String(dispatch.note || '').trim(),
    },
    returnNumber: {
      status: String(returnNumber.status || 'pending').trim() || 'pending',
      returnedAt: returnNumber.returnedAt || 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: mapKuaishouCloudRebindContext(record.rebind),
    notes: String(record.notes || '').trim(),
  }
}

export function mapKuaishouCloudTaskStateProjection(
  task: TaskLike | null | undefined,
  options: KuaishouCloudFulfillmentMapOptions = {},
): JsonRecord | null {
  if (!task || String(task.executor_key || '').trim() !== 'kuaishou_ct_assisted') {
    return null
  }

  const hasProjection = [
    task.kuaishou_ticket_status,
    task.kuaishou_ticket_code_masked,
    task.kuaishou_bind_url,
    task.kuaishou_vn_phone,
    task.kuaishou_role_name,
    task.kuaishou_role_id,
    task.kuaishou_dispatch_status,
    task.kuaishou_consume_status,
    task.kuaishou_source_key,
  ].some((value) => String(value || '').trim())

  if (!hasProjection) {
    return null
  }

  const sourceKey = String(task.kuaishou_source_key || '').trim()
  const roleName = String(task.kuaishou_role_name || '').trim()
  const roleId = String(task.kuaishou_role_id || '').trim()

  return {
    flowType: 'kuaishou_cloud_fulfillment',
    configId: '',
    internalSkuCode: '',
    internalSkuName: '',
    ticket: {
      code: String(task.kuaishou_ticket_code_masked || '').trim(),
      capturedAt: null,
      status: String(task.kuaishou_ticket_status || 'pending').trim() || 'pending',
      verifiedAt: null,
      goodsTitle: '',
      leftCount: 0,
    },
    binding: {
      prepareStatus: task.kuaishou_bind_url ? 'ready' : 'pending',
      cloudSourceKeys: sourceKey ? [sourceKey] : [],
      cloudSourceLabels: sourceKey
        ? [resolveCloudSourceLabel(sourceKey, options.cloudSourceLabelMap)]
        : [],
      resolvedSourceKey: sourceKey,
      resolvedSourceLabel: sourceKey
        ? resolveCloudSourceLabel(sourceKey, options.cloudSourceLabelMap)
        : '',
      skuId: 0,
      skuName: '',
      vnKey: '',
      vnId: 0,
      vnPhone: String(task.kuaishou_vn_phone || '').trim(),
      bindUrl: String(task.kuaishou_bind_url || '').trim(),
      bindPreparedAt: null,
      bindExpiresAt: null,
      bindProbeAt: null,
      bindProbeStatus: '',
      bindProbeMessage: '',
    },
    role: {
      status: roleName || roleId ? 'ready' : 'pending',
      name: roleName,
      rid: roleId,
      refreshedAt: null,
      errorMessage: '',
      rawInfo: null,
    },
    purchase: {
      autoBuyEnabled: true,
      minAssetReserve: 0,
      usedKnapsack: false,
      purchaseTriggered: false,
      assetBefore: 0,
      assetAfter: 0,
      purchaseAt: null,
    },
    dispatch: {
      status: String(task.kuaishou_dispatch_status || 'pending').trim() || 'pending',
      dispatchAt: null,
      sendType: 0,
      note: '',
    },
    returnNumber: {
      status: 'pending',
      returnedAt: null,
      autoReturnEnabled: false,
    },
    consume: {
      status: String(task.kuaishou_consume_status || 'pending').trim() || 'pending',
      shopId: '',
      shopName: '',
      autoConsumeEnabled: false,
      consumedAt: null,
      errorMessage: '',
    },
    rebind: {
      currentAttempt: 0,
      history: [],
    },
    notes: '',
  }
}

function resolveCloudSourceLabel(sourceKey: string, labelMap: CloudSourceLabelMap | undefined) {
  const key = String(sourceKey || '').trim()
  if (!key) return ''

  const label = labelMap instanceof Map ? labelMap.get(key) : labelMap?.[key]

  return String(label || key).trim() || key
}

function mapKuaishouCloudRebindContext(value: unknown): JsonRecord {
  const record = value && typeof value === 'object' ? (value as JsonRecord) : {}
  const history = Array.isArray(record.history) ? record.history : []

  return {
    currentAttempt: Math.max(0, Number(record.currentAttempt || 0) || 0),
    history: history.map((item) => mapKuaishouCloudRebindHistoryItem(item)).filter(Boolean),
  }
}

function mapKuaishouCloudRebindHistoryItem(value: unknown): JsonRecord | null {
  if (!value || typeof value !== 'object') {
    return null
  }

  const record = value as JsonRecord
  const oldBinding =
    record.oldBinding && typeof record.oldBinding === 'object'
      ? (record.oldBinding as JsonRecord)
      : {}
  const newBinding =
    record.newBinding && typeof record.newBinding === 'object'
      ? (record.newBinding as JsonRecord)
      : null

  return {
    attempt: Math.max(1, Number(record.attempt || 1) || 1),
    source: String(record.source || '').trim(),
    requestedAt: record.requestedAt || null,
    requestedBy:
      record.requestedBy && typeof record.requestedBy === 'object' ? record.requestedBy : null,
    status: String(record.status || '').trim(),
    errorMessage: String(record.errorMessage || '').trim(),
    oldBinding: {
      vnKey: String(oldBinding.vnKey || '').trim(),
      vnId: Number(oldBinding.vnId || 0) || 0,
      vnPhone: String(oldBinding.vnPhone || '').trim(),
      bindUrl: String(oldBinding.bindUrl || '').trim(),
      bindPreparedAt: oldBinding.bindPreparedAt || null,
      bindExpiresAt: oldBinding.bindExpiresAt || null,
      roleName: String(oldBinding.roleName || '').trim(),
      roleId: String(oldBinding.roleId || '').trim(),
    },
    newBinding: newBinding
      ? {
          vnKey: String(newBinding.vnKey || '').trim(),
          vnId: Number(newBinding.vnId || 0) || 0,
          vnPhone: String(newBinding.vnPhone || '').trim(),
          bindUrl: String(newBinding.bindUrl || '').trim(),
          bindPreparedAt: newBinding.bindPreparedAt || null,
          bindExpiresAt: newBinding.bindExpiresAt || null,
        }
      : null,
  }
}

export function mapRedeemResolutionContext(value: unknown): JsonRecord | null {
  if (!value || typeof value !== 'object') {
    return null
  }

  const record = value as JsonRecord
  const attempts = Array.isArray(record.attempts)
    ? record.attempts
        .map((attempt) => mapRedeemResolutionAttempt(attempt))
        .filter((attempt): attempt is RedeemResolutionAttempt => Boolean(attempt))
    : []

  return {
    status: String(record.status || '').trim(),
    taskStatus: String(record.taskStatus || '').trim(),
    replacementCount: Math.max(0, Number(record.replacementCount || 0)),
    finishedAt: record.finishedAt || null,
    attempts,
  }
}

export function getTaskPrimaryClaimTokenId(task: TaskLike | null | undefined): number | null {
  const value = Number(task?.primary_claim_token_id || 0)
  return Number.isFinite(value) && value > 0 ? value : null
}

export function createAdminViewerContext(
  session: AdminViewerSessionInput | null = null,
): AdminViewerContext {
  const role = normalizeAdminRole(session?.role)

  return {
    role,
    canViewSensitiveTaskData: role === 'admin' || role === 'operator',
    canManageTaskLifecycle: role === 'admin' || role === 'operator',
    canOperateAssistedTask: role === 'admin' || role === 'operator' || role === 'support',
    canOperateKuaishouIndustryVoucher:
      role === 'admin' || role === 'operator' || role === 'support',
  }
}

export async function resolveAdminTaskScreenshotUrl(
  task: TaskLike,
  _viewerContext: AdminViewerContext,
): Promise<string> {
  if (task.screenshot_path || task.runtime_session_id) {
    return `/api/v1/admin/tasks/${task.id}/screenshot`
  }

  return ''
}

export function canViewerCloseTask(task: TaskLike, viewerContext: AdminViewerContext): boolean {
  if (!['admin', 'operator', 'support'].includes(viewerContext.role)) {
    return false
  }

  return !isTaskFinalStatus(task?.task_status)
}

export function resolveDisplayShopName(
  provider: unknown,
  shopId: unknown,
  shopName: unknown,
): string {
  const normalizedShopName = String(shopName || '').trim()
  if (normalizedShopName) {
    return normalizedShopName
  }

  const normalizedShopId = String(shopId || '').trim()
  if (!normalizedShopId) {
    return ''
  }

  return normalizedShopId
}

export function parseTaskContext(task: TaskLike | null | undefined): JsonRecord {
  return parseTaskContextValue(task)
}

export function parseTaskState(task: TaskLike | null | undefined): JsonRecord {
  return parseTaskStateValue(task)
}

export function resolveOrderItemTitle(item: Partial<OrderItemRow> | null | undefined): string {
  if (!item) {
    return ''
  }

  const spec = safeParseJson(item.spec_json)

  return pickFirstNonEmpty([
    spec.title,
    spec.Title,
    spec.itemTitle,
    spec.item_title,
    spec.goods_name,
    spec.goodsName,
    item.sku_name,
    item.sku_code,
  ])
}

export function resolveOrderItemDeliveryMode(
  tasks: TaskLike[] | null | undefined,
  orderItemId: number | string,
): string {
  const task = (Array.isArray(tasks) ? tasks : []).find(
    (item) => item.order_item_id === orderItemId,
  )

  if (!task) {
    return ''
  }

  if (String(task.executor_key || '').trim() === 'manual_dispatch') {
    return 'manual_dispatch'
  }

  if (String(task.executor_key || '').trim() === 'kuaishou_ct_assisted') {
    return 'kuaishou_cloud'
  }

  if (
    task.requires_claim ||
    getTaskPrimaryClaimTokenId(task) ||
    task.primary_claim_token ||
    task.claim_token
  ) {
    return 'claim_link'
  }

  return String(task.executor_key || '').trim()
}

export function isManualDispatchTask(task: TaskLike | null | undefined): boolean {
  return String(task?.executor_key || '').trim() === 'manual_dispatch'
}

export function canRegenerateClaimLinkForViewer(
  task: TaskLike,
  viewerContext: AdminViewerContext,
): boolean {
  if (isManualDispatchTask(task)) {
    return false
  }

  const baseAllowed = canRegenerateClaimLinkStatus(task.task_status)

  if (!baseAllowed) {
    return false
  }

  if (viewerContext.canManageTaskLifecycle) {
    return true
  }

  return false
}

function mapRedeemResolutionAttempt(value: unknown): RedeemResolutionAttempt | null {
  if (!value || typeof value !== 'object') {
    return null
  }

  const record = value as JsonRecord
  return {
    attempt: Math.max(1, Number(record.attempt || 1)),
    codeMasked: String(record.codeMasked || '').trim(),
    credentialType: String(record.credentialType || '').trim(),
    outcome: String(record.outcome || '').trim(),
    resultCode: String(record.resultCode || '').trim(),
    resultMessage: String(record.resultMessage || '').trim(),
  }
}

function pickFirstNonEmpty(values: unknown[]): string {
  for (const value of values) {
    if (typeof value === 'string' && value.trim()) {
      return value.trim()
    }

    if (typeof value === 'number' && Number.isFinite(value)) {
      return String(value)
    }
  }

  return ''
}
