import { getAgisoShopConfig } from '../platforms/agiso/shop-config-service.js'
import { safeParseJson } from './admin-query-utils.js'
import { normalizeAdminRole } from './admin-auth-service.js'
import {
  parseTaskContext as parseTaskContextValue,
  parseTaskState as parseTaskStateValue,
} from '../../utils/task-json.js'

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

type JsonRecord = Record<string, any>

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

export type AdminViewerContext = {
  role: string
  allowedInventoryGroupCodes: string[] | null
  restrictInventoryGroups: boolean
  canViewSensitiveTaskData: boolean
  canManageSensitiveInventory: boolean
  canManageTaskLifecycle: boolean
  canOperateAssistedTask: boolean
}

type RedeemResolutionAttempt = {
  attempt: number
  inventoryItemId: number | null
  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): JsonRecord | null {
  if (!value || typeof value !== 'object') {
    return null
  }

  const record = value as JsonRecord
  const ticket = record.ticket && typeof record.ticket === 'object' ? record.ticket : {}
  const binding = record.binding && typeof record.binding === 'object' ? record.binding : {}
  const role = record.role && typeof record.role === 'object' ? record.role : {}
  const purchase = record.purchase && typeof record.purchase === 'object' ? record.purchase : {}
  const dispatch = record.dispatch && typeof record.dispatch === 'object' ? record.dispatch : {}
  const returnNumber = record.returnNumber && typeof record.returnNumber === 'object' ? record.returnNumber : {}
  const consume = record.consume && typeof record.consume === 'object' ? record.consume : {}

  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',
      cloudSourceKey: String(binding.cloudSourceKey || '').trim(),
      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(),
    },
    role: {
      status: String(role.status || 'pending').trim() || 'pending',
      name: String(role.name || '').trim(),
      rid: String(role.rid || '').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(),
    },
    notes: String(record.notes || '').trim(),
  }
}

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 getTaskPrimaryInventoryItemId(task: TaskLike | null | undefined): number | null {
  const value = Number(task?.primary_inventory_item_id || 0)
  return Number.isFinite(value) && value > 0 ? value : null
}

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)
  const allowedInventoryGroupCodes = role === 'support'
    ? normalizeInventoryGroupCodes(session?.allowedInventoryGroups)
    : null

  return {
    role,
    allowedInventoryGroupCodes,
    restrictInventoryGroups: role === 'support',
    canViewSensitiveTaskData: role === 'admin' || role === 'operator',
    canManageSensitiveInventory: role === 'admin',
    canManageTaskLifecycle: role === 'admin' || role === 'operator',
    canOperateAssistedTask: role === 'admin' || role === 'operator' || role === 'support',
  }
}

export async function resolveAdminTaskScreenshotUrl(
  task: TaskLike,
  viewerContext: AdminViewerContext,
): Promise<string> {
  if (task.screenshot_path || task.browser_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 !['redeemed', 'completed', 'closed'].includes(String(task?.task_status || '').trim())
}

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 ''
  }

  if (String(provider || '').trim().toLowerCase() === 'agiso') {
    const configuredName = String(getAgisoShopConfig(normalizedShopId)?.shopName || '').trim()
    if (configuredName) {
      return configuredName
    }
  }

  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 isAssistedClaimTask(task: TaskLike | null | undefined): boolean {
  return String(task?.executor_key || '').trim() === 'tencent_claim_assisted'
}

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

  const baseAllowed = ['link_generated', 'claimed', 'role_confirmed', 'retry_pending', 'manual_review'].includes(task.task_status)

  if (!baseAllowed) {
    return false
  }

  if (viewerContext.canManageTaskLifecycle) {
    return true
  }

  return viewerContext.canOperateAssistedTask
    && isAssistedClaimTask(task)
    && canViewerAccessTaskInventoryGroup(task, viewerContext)
}

export function canViewerConfirmAssistedRole(task: TaskLike, viewerContext: AdminViewerContext): boolean {
  if (!viewerContext.canOperateAssistedTask || !isAssistedClaimTask(task) || !canViewerAccessTaskInventoryGroup(task, viewerContext)) {
    return false
  }

  return String(task?.task_status || '').trim() === 'claimed'
}

export function canViewerRedeemAssistedTask(task: TaskLike, viewerContext: AdminViewerContext): boolean {
  if (!viewerContext.canOperateAssistedTask || !isAssistedClaimTask(task) || !canViewerAccessTaskInventoryGroup(task, viewerContext)) {
    return false
  }

  return ['role_confirmed', 'redeeming'].includes(String(task?.task_status || '').trim())
}

export function canViewerAccessTaskInventoryGroup(
  task: TaskLike,
  viewerContext: AdminViewerContext = createAdminViewerContext(),
): boolean {
  if (!viewerContext.restrictInventoryGroups) {
    return true
  }

  const allowedInventoryGroupCodes = Array.isArray(viewerContext.allowedInventoryGroupCodes)
    ? viewerContext.allowedInventoryGroupCodes
    : []

  if (allowedInventoryGroupCodes.length === 0) {
    return false
  }

  const taskContext = parseTaskContext(task)
  const inventoryGroupCode = String(taskContext?.inventoryGroupCode || '').trim()

  if (!inventoryGroupCode) {
    return true
  }

  return allowedInventoryGroupCodes.includes(inventoryGroupCode)
}

export function normalizeInventoryGroupCodes(values: unknown): string[] {
  return Array.from(new Set((Array.isArray(values) ? values : [])
    .map((value) => String(value || '').trim())
    .filter(Boolean)))
}

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)),
    inventoryItemId: Number(record.inventoryItemId || 0) || null,
    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 ''
}
