import crypto from 'node:crypto'

import { runtimeConfig } from '../../../../config/runtime.js'
import { getAgisoShopConfig } from '../shop-config-service.js'
import { logWebhook } from '../../../../utils/logger.js'
import { nowIso } from '../../../../utils/time.js'
import { parseAmountToFen } from '../../../../utils/money.js'
import { parseJsonObject } from '../../../../utils/json.js'

const DEFAULT_DETAIL_TIMEOUT_MS = 5000

type JsonObject = Record<string, unknown>

type AgisoParsedTrade = {
  provider?: string
  platform?: string
  shopId?: string
  shopName?: string
  platformOrderId?: string
  totalAmount?: number
  paidAt?: string | null
  buyerId?: string
  buyerName?: string
  receiverContact?: string
  rawPayload?: JsonObject
  items?: AgisoOrderItem[]
  [key: string]: unknown
}

type AgisoOrderItem = {
  skuCode: string
  skuName: string
  externalItemId: string
  externalSkuCode: string
  externalSkuName: string
  quantity: number
  spec: JsonObject
  snapshot: {
    externalItemId: string
    externalSkuCode: string
    externalSkuName: string
  }
}

type AgisoDetailQueryInput = {
  shopId?: string
  platformOrderId?: string
  requestId?: string
}

type FetchResponseLike = {
  status: number
  text: () => Promise<string>
}

type FetchWithTimeout = (
  url: string,
  options: RequestInit,
  timeoutMs: number,
) => Promise<FetchResponseLike>

type AgisoDetailDeps = {
  fetchWithTimeout?: FetchWithTimeout
  queryAgisoXianyuOrderDetail?: (
    input: Required<AgisoDetailQueryInput>,
  ) => Promise<AgisoDetailResult>
}

type AgisoTradeDetailConfig = {
  endpoint: string
  apiVersion: string
  accessToken: string
  appSecret: string
  timeoutMs: number
}

type AgisoShopConfig = {
  accessToken?: string
  appSecret?: string
  tradeDetailEndpoint?: string
  tradeDetailApiVersion?: string
  tradeDetailTimeoutMs?: string | number
}

type DeliveryState = {
  shipped: boolean
  shipTime: number
  orderStatus: number
}

type AgisoDetailResult = {
  success: boolean
  reason: string
  errorMessage: string
  responseStatus: number
  payload: JsonObject
  detailPayload: JsonObject
  totalAmountFen: number
  shipped: boolean
  shipTime: number
  orderStatus: number
}

type ExternalSkuDescriptor = {
  raw: string
  externalSkuCode: string
  externalSkuName: string
}

export async function enrichAgisoXianyuTradeOrder(parsed: AgisoParsedTrade, { requestId = '' } = {}) {
  return enrichAgisoXianyuTradeOrderWithDeps(parsed, { requestId })
}

export async function enrichAgisoXianyuTradeOrderWithDeps(
  parsed: AgisoParsedTrade,
  { requestId = '' } = {},
  deps: AgisoDetailDeps = {},
) {
  const queryOrderDetail = deps.queryAgisoXianyuOrderDetail || queryAgisoXianyuOrderDetail

  if (!shouldHydrateAgisoXianyuTradeOrder(parsed)) {
    return { parsed, enriched: false, reason: 'not_needed' }
  }

  const detailResult = await queryOrderDetail({
    shopId: parsed.shopId,
    platformOrderId: parsed.platformOrderId,
    requestId,
  })

  if (!detailResult.success) {
    return {
      parsed,
      enriched: false,
      reason: detailResult.reason,
      errorMessage: detailResult.errorMessage,
    }
  }

  const detailPayload = detailResult.detailPayload
  if (detailResult.totalAmountFen <= 0) {
    return {
      parsed: mergeEnrichedTrade(parsed, detailPayload, { keepOriginalAmount: true }),
      enriched: false,
      reason: 'amount_still_missing',
    }
  }

  const merged = mergeEnrichedTrade(parsed, detailPayload)
  logWebhook('[agiso/xianyu/order-detail]', 'Agiso 咸鱼订单详情补查成功', {
    requestId,
    platform: parsed.platform,
    shopId: parsed.shopId,
    platformOrderId: parsed.platformOrderId,
    totalAmountFen: merged.totalAmount,
    paidAt: merged.paidAt,
  })

  return {
    parsed: merged,
    enriched: true,
    totalAmountFen: merged.totalAmount,
  }
}

export async function queryAgisoXianyuOrderDetail({ shopId = '', platformOrderId = '', requestId = '' } = {}) {
  return queryAgisoXianyuOrderDetailWithDeps({ shopId, platformOrderId, requestId })
}

export async function queryAgisoXianyuOrderDetailWithDeps(
  { shopId = '', platformOrderId = '', requestId = '' } = {},
  deps: AgisoDetailDeps = {},
): Promise<AgisoDetailResult> {
  const requestWithTimeout = deps.fetchWithTimeout || fetchWithTimeout
  const normalizedShopId = String(shopId || '').trim()
  const normalizedPlatformOrderId = String(platformOrderId || '').trim()
  const config = resolveAgisoXianyuTradeDetailConfig(normalizedShopId)

  if (!config.endpoint || !config.accessToken || !config.appSecret) {
    logWebhook('[agiso/xianyu/order-detail]', '跳过 Agiso 咸鱼订单详情补查：缺少必要配置', {
      requestId,
      platform: 'xianyu',
      shopId: normalizedShopId,
      platformOrderId: normalizedPlatformOrderId,
      hasEndpoint: Boolean(config.endpoint),
      hasAccessToken: Boolean(config.accessToken),
      hasAppSecret: Boolean(config.appSecret),
    }, { level: 'warn' })

    return createFailedDetailResult('missing_config')
  }

  const requestBody = buildRequestBody({
    platformOrderId: normalizedPlatformOrderId,
    appSecret: config.appSecret,
  })
  const requestHeaders = buildRequestHeaders({
    accessToken: config.accessToken,
    apiVersion: config.apiVersion,
  })

  logWebhook('[agiso/xianyu/order-detail]', '开始补查 Agiso 咸鱼订单详情', {
    requestId,
    platform: 'xianyu',
    shopId: normalizedShopId,
    platformOrderId: normalizedPlatformOrderId,
    endpoint: config.endpoint,
  })

  try {
    const response = await requestWithTimeout(config.endpoint, {
      method: 'POST',
      headers: requestHeaders,
      body: new URLSearchParams(requestBody).toString(),
    }, config.timeoutMs)
    const rawText = await response.text()
    const payload = parseJsonObject(rawText, { preserveLargeIntegers: true }) as JsonObject
    const detailPayload = extractAgisoDetailPayload(payload)
    const delivery = resolveAgisoXianyuOrderDeliveryState(detailPayload)
    const totalAmountFen = resolveTotalAmountFen(detailPayload)

    if (response.status < 200 || response.status >= 300) {
      const message = resolveAgisoDetailErrorMessage(payload, rawText, response.status)
      logWebhook('[agiso/xianyu/order-detail]', 'Agiso 咸鱼订单详情补查失败', {
        requestId,
        platform: 'xianyu',
        shopId: normalizedShopId,
        platformOrderId: normalizedPlatformOrderId,
        responseStatus: response.status,
        errorMessage: message,
      }, { level: 'warn' })

      return createFailedDetailResult('http_error', {
        responseStatus: response.status,
        payload,
        detailPayload,
        errorMessage: message,
        totalAmountFen,
        delivery,
      })
    }

    if (!isAgisoDetailSuccess(payload)) {
      const message = resolveAgisoDetailErrorMessage(payload, rawText, response.status)
      logWebhook('[agiso/xianyu/order-detail]', 'Agiso 咸鱼订单详情补查返回业务失败', {
        requestId,
        platform: 'xianyu',
        shopId: normalizedShopId,
        platformOrderId: normalizedPlatformOrderId,
        responseStatus: response.status,
        errorMessage: message,
        response: payload,
      }, { level: 'warn' })

      return createFailedDetailResult('business_error', {
        responseStatus: response.status,
        payload,
        detailPayload,
        errorMessage: message,
        totalAmountFen,
        delivery,
      })
    }

    if (!isPlainObject(detailPayload) || Object.keys(detailPayload).length === 0) {
      logWebhook('[agiso/xianyu/order-detail]', 'Agiso 咸鱼订单详情补查未返回可用订单体', {
        requestId,
        platform: 'xianyu',
        shopId: normalizedShopId,
        platformOrderId: normalizedPlatformOrderId,
        responseStatus: response.status,
      }, { level: 'warn' })

      return createFailedDetailResult('empty_detail_payload', {
        responseStatus: response.status,
        payload,
      })
    }

    if (totalAmountFen <= 0) {
      logWebhook('[agiso/xianyu/order-detail]', 'Agiso 咸鱼订单详情已返回，但仍未解析出金额', {
        requestId,
        platform: 'xianyu',
        shopId: normalizedShopId,
        platformOrderId: normalizedPlatformOrderId,
        responseStatus: response.status,
        detailKeys: Object.keys(detailPayload),
      }, { level: 'warn' })
    }

    return {
      success: true,
      reason: '',
      errorMessage: '',
      responseStatus: response.status,
      payload: isPlainObject(payload) ? payload : {},
      detailPayload,
      totalAmountFen,
      shipped: delivery.shipped,
      shipTime: delivery.shipTime,
      orderStatus: delivery.orderStatus,
    }
  } catch (error) {
    const message = error instanceof Error ? error.message : String(error || 'Agiso 咸鱼订单详情补查失败')
    logWebhook('[agiso/xianyu/order-detail]', 'Agiso 咸鱼订单详情补查异常', {
      requestId,
      platform: 'xianyu',
      shopId: normalizedShopId,
      platformOrderId: normalizedPlatformOrderId,
      errorMessage: message,
    }, { level: 'warn' })

    return createFailedDetailResult('request_failed', {
      errorMessage: message,
    })
  }
}

function shouldHydrateAgisoXianyuTradeOrder(parsed: AgisoParsedTrade | null | undefined): boolean {
  return parsed?.provider === 'agiso'
    && parsed?.platform === 'xianyu'
    && String(parsed?.platformOrderId || '').trim()
    && Number(parsed?.totalAmount || 0) <= 0
}

function resolveAgisoXianyuTradeDetailConfig(shopId: string): AgisoTradeDetailConfig {
  const baseConfig = runtimeConfig.platforms.agiso.tradeDetail
  const shopConfig = (getAgisoShopConfig(shopId) || {}) as AgisoShopConfig

  return {
    endpoint: String(shopConfig.tradeDetailEndpoint || baseConfig.endpoint || '').trim(),
    apiVersion: String(shopConfig.tradeDetailApiVersion || baseConfig.apiVersion || '1').trim() || '1',
    accessToken: String(shopConfig.accessToken || '').trim(),
    appSecret: String(shopConfig.appSecret || runtimeConfig.platforms?.agiso?.appSecret || '').trim(),
    timeoutMs: normalizePositiveInteger(shopConfig.tradeDetailTimeoutMs || baseConfig.timeoutMs, DEFAULT_DETAIL_TIMEOUT_MS),
  }
}

function buildRequestHeaders({
  accessToken,
  apiVersion,
}: { accessToken: string; apiVersion: string }): Record<string, string> {
  return {
    Authorization: `Bearer ${accessToken}`,
    ApiVersion: apiVersion,
    'content-type': 'application/x-www-form-urlencoded; charset=utf-8',
  }
}

function buildRequestBody({
  platformOrderId,
  appSecret,
}: { platformOrderId: string; appSecret: string }): Record<string, string> {
  const timestamp = String(Math.floor(Date.now() / 1000))
  const payload: Record<string, string> = {
    tid: String(platformOrderId || '').trim(),
    timestamp,
  }

  payload.sign = generateSign(payload, appSecret)
  return payload
}

function generateSign(params: Record<string, string>, appSecret: string): string {
  const sortedEntries = Object.entries(params).sort(([left], [right]) => left.localeCompare(right))
  let raw = appSecret

  for (const [key, value] of sortedEntries) {
    raw += `${key}${value}`
  }

  raw += appSecret

  return crypto.createHash('md5').update(raw, 'utf8').digest('hex').toLowerCase()
}

function extractAgisoDetailPayload(payload) {
  if (!isPlainObject(payload)) {
    return {}
  }

  for (const key of ['Data', 'data', 'Result', 'result', 'Trade', 'trade', 'Order', 'order']) {
    if (isPlainObject(payload[key])) {
      return payload[key]
    }
  }

  return payload
}

export function resolveAgisoXianyuOrderDeliveryState(payload) {
  const shipTime = normalizeTimestampValue(pickFirstNonEmpty([
    payload?.ship_time,
    payload?.shipTime,
    payload?.ShipTime,
    payload?.delivery_time,
    payload?.deliveryTime,
  ]))
  const orderStatus = normalizeInteger(
    pickFirstNonEmpty([
      payload?.order_status,
      payload?.orderStatus,
      payload?.status,
    ]),
  )

  return {
    shipped: shipTime > 0 || orderStatus >= 3,
    shipTime,
    orderStatus,
  }
}

function resolveTotalAmountFen(payload) {
  const fenAmount = normalizeFenInteger(pickFirstNonEmpty([
    payload.payment,
    payload.Payment,
    payload.post_fee,
    payload.postFee,
    payload.item?.price,
  ]))

  if (fenAmount > 0) {
    return fenAmount
  }

  return parseAmountToFen(pickFirstNonEmpty([
    payload.total_fee,
    payload.totalFee,
    payload.TotalFee,
    payload.pay_fee,
    payload.payFee,
    payload.PayFee,
    payload.actual_fee,
    payload.actualFee,
    payload.ActualFee,
    payload.total_amount,
    payload.totalAmount,
    payload.Amount,
    payload.amount,
  ]))
}

function resolvePaidAt(payload, fallbackValue) {
  const providerPaidAt = normalizeProviderDateTime(
    pickFirstNonEmpty([
      payload.paid_at,
      payload.paidAt,
      payload.pay_time,
      payload.payTime,
      payload.PayTime,
      fallbackValue,
    ]),
  )

  if (providerPaidAt) {
    return providerPaidAt
  }

  const rawStatus = normalizeInteger(
    pickFirstNonEmpty([payload.order_status, payload.orderStatus, payload.status]),
  )

  if (rawStatus === 2 || rawStatus === 3 || rawStatus === 4) {
    return nowIso()
  }

  return null
}

function mergeEnrichedTrade(parsed, detailPayload, { keepOriginalAmount = false } = {}) {
  const mergedPayload = {
    ...parsed.rawPayload,
    _agisoTradeDetail: detailPayload,
  }
  const totalAmountFen = resolveTotalAmountFen(detailPayload)
  const mergedItems = normalizeOrderItems(detailPayload, normalizeOrderItems(parsed.rawPayload, parsed.items))

  return {
    ...parsed,
    buyerId: pickFirstNonEmpty([
      parsed.buyerId,
      detailPayload.encryption_buyer_id,
      detailPayload.buyer_id,
      detailPayload.buyerId,
      detailPayload.BuyerId,
      detailPayload.BuyerOpenUid,
      detailPayload.buyer_open_uid,
    ]),
    buyerName: pickFirstNonEmpty([
      parsed.buyerName,
      detailPayload.buyer_name,
      detailPayload.buyerName,
      detailPayload.BuyerName,
      detailPayload.buyer_nick,
      detailPayload.nick,
      detailPayload.BuyerNick,
    ]),
    receiverContact: pickFirstNonEmpty([
      parsed.receiverContact,
      detailPayload.receiver_contact,
      detailPayload.receiverContact,
      detailPayload.receiver_mobile,
      detailPayload.receiverMobile,
      detailPayload.mobile,
      detailPayload.phone,
    ]),
    shopName: pickFirstNonEmpty([
      parsed.shopName,
      detailPayload.shop_name,
      detailPayload.shopName,
      detailPayload.ShopName,
      detailPayload.seller_name,
      detailPayload.sellerName,
      detailPayload.seller_nick,
      detailPayload.sellerNick,
      detailPayload.SellerNick,
    ]),
    totalAmount: keepOriginalAmount ? parsed.totalAmount : (totalAmountFen || parsed.totalAmount),
    paidAt: parsed.paidAt || resolvePaidAt(detailPayload, parsed.paidAt),
    rawPayload: mergedPayload,
    items: mergedItems,
  }
}

function normalizeOrderItems(payload, fallbackItems = []) {
  const candidates = [
    payload.item ? [payload.item] : null,
    payload.items,
    payload.Items,
    payload.orders,
    payload.Orders,
    payload.order_list,
    payload.OrderList,
  ]
  const items = candidates.find((item) => Array.isArray(item) && item.length > 0)

  if (!Array.isArray(items) || items.length === 0) {
    return Array.isArray(fallbackItems) ? fallbackItems : []
  }

  return items.map((item) => {
    const source = isPlainObject(item) ? item : {}
    const skuDescriptor = parseExternalSkuDescriptor(
      pickFirstNonEmpty([
        source.sku,
        source.Sku,
        payload.sku,
        payload.Sku,
      ]),
    )
    const skuCode = pickFirstNonEmpty([
      skuDescriptor.externalSkuCode,
      source.OuterSkuId,
      source.outerSkuId,
      source.outer_sku_id,
      source.OuterIid,
      source.outerIid,
      source.outer_iid,
      source.sku_code,
      source.skuCode,
      source.goods_sku,
      source.item_id,
      source.itemId,
      source.goods_id,
      source.goodsId,
      source.NumIid,
      source.num_iid,
      payload.item_id,
      payload.itemId,
    ])
    const externalItemId = pickFirstNonEmpty([
      source.item_id,
      source.itemId,
      source.goods_id,
      source.goodsId,
      payload.item_id,
      payload.itemId,
    ])
    const externalSkuName = pickFirstNonEmpty([
      skuDescriptor.externalSkuName,
      source.Title,
      source.title,
      source.sku_name,
      source.skuName,
      source.goods_name,
      source.goodsName,
      skuCode,
    ])

    return {
      skuCode,
      skuName: externalSkuName,
      externalItemId,
      externalSkuCode: pickFirstNonEmpty([skuDescriptor.externalSkuCode, skuCode, externalItemId]),
      externalSkuName,
      quantity: Math.max(
        1,
        normalizeInteger(
          pickFirstNonEmpty([
            source.quantity,
            source.num,
            source.Num,
            source.buy_amount,
            payload.quantity,
            payload.num,
            payload.Num,
          ]),
        ) || 1,
      ),
      spec: source,
      snapshot: {
        externalItemId,
        externalSkuCode: pickFirstNonEmpty([skuDescriptor.externalSkuCode, skuCode, externalItemId]),
        externalSkuName,
      },
    }
  })
}

function parseExternalSkuDescriptor(rawValue) {
  const raw = String(rawValue || '').trim()
  if (!raw) {
    return {
      raw,
      externalSkuCode: '',
      externalSkuName: '',
    }
  }

  const parts = raw.split('|').map((part) => String(part || '').trim()).filter(Boolean)
  let externalSkuCode = ''
  let externalSkuName = ''

  for (const part of parts) {
    if (!externalSkuCode && !part.includes(':') && !part.includes('：')) {
      externalSkuCode = part
      continue
    }

    const separatorIndex = Math.max(part.indexOf(':'), part.indexOf('：'))
    if (separatorIndex >= 0) {
      const label = part.slice(0, separatorIndex).trim()
      const value = part.slice(separatorIndex + 1).trim()
      if (value && ['商品名称', '商品名', 'sku名称', '规格名称', '名称', '商品'].includes(label)) {
        externalSkuName = value
      }
    }
  }

  if (!externalSkuCode && parts[0]) {
    externalSkuCode = parts[0]
  }

  if (!externalSkuName) {
    externalSkuName = parts
      .map((part) => {
        const separatorIndex = Math.max(part.indexOf(':'), part.indexOf('：'))
        return separatorIndex >= 0 ? part.slice(separatorIndex + 1).trim() : ''
      })
      .find(Boolean) || ''
  }

  return {
    raw,
    externalSkuCode,
    externalSkuName,
  }
}

function isAgisoDetailSuccess(payload) {
  if (!payload || typeof payload !== 'object' || Object.keys(payload).length === 0) {
    return false
  }

  if (payload.IsSuccess === true) {
    return true
  }

  if (Number(payload.Error_Code) === 0) {
    return true
  }

  if (Number(payload.code) === 0) {
    return true
  }

  if (Number(payload.success) === 1 || payload.success === true) {
    return true
  }

  return false
}

function resolveAgisoDetailErrorMessage(payload, rawText, statusCode) {
  if (payload && typeof payload === 'object') {
    for (const value of [payload.Error_Msg, payload.msg, payload.message, payload.error]) {
      const normalized = String(value || '').trim()
      if (normalized) {
        return normalized
      }
    }
  }

  const text = String(rawText || '').trim()
  return text || `Agiso 咸鱼订单详情接口失败，HTTP ${statusCode}`
}

async function fetchWithTimeout(url, options, timeoutMs) {
  const controller = new AbortController()
  const timer = setTimeout(() => controller.abort(new Error('Agiso 咸鱼订单详情请求超时')), timeoutMs)

  try {
    return await fetch(url, {
      ...options,
      signal: controller.signal,
    })
  } finally {
    clearTimeout(timer)
  }
}

function normalizeProviderDateTime(value) {
  const normalized = String(value || '').trim()

  if (!normalized) {
    return null
  }

  if (/^\d{10,13}$/.test(normalized)) {
    const timestamp = normalized.length === 13 ? Number(normalized) : Number(normalized) * 1000

    if (Number.isFinite(timestamp)) {
      return new Date(timestamp).toISOString()
    }
  }

  const isoLike = normalized.replace(' ', 'T')
  const parsed = Date.parse(isoLike)

  if (Number.isNaN(parsed)) {
    return null
  }

  return new Date(parsed).toISOString()
}

function normalizeInteger(value) {
  const parsed = Number(value)
  return Number.isFinite(parsed) ? Math.round(parsed) : 0
}

function normalizePositiveInteger(value, fallbackValue) {
  const parsed = Number(value)
  return Number.isFinite(parsed) && parsed > 0 ? Math.round(parsed) : fallbackValue
}

function normalizeFenInteger(value) {
  const parsed = Number(value)
  return Number.isFinite(parsed) && parsed > 0 ? Math.round(parsed) : 0
}

function normalizeTimestampValue(value) {
  const parsed = Number(value)
  return Number.isFinite(parsed) && parsed > 0 ? Math.round(parsed) : 0
}

function createFailedDetailResult(reason, {
  responseStatus = 0,
  payload = {},
  detailPayload = {},
  errorMessage = '',
  totalAmountFen = 0,
  delivery = { shipped: false, shipTime: 0, orderStatus: 0 },
} = {}) {
  return {
    success: false,
    reason: String(reason || '').trim(),
    errorMessage: String(errorMessage || '').trim(),
    responseStatus: Number(responseStatus || 0),
    payload: isPlainObject(payload) ? payload : {},
    detailPayload: isPlainObject(detailPayload) ? detailPayload : {},
    totalAmountFen: Number(totalAmountFen || 0),
    shipped: Boolean(delivery?.shipped),
    shipTime: Number(delivery?.shipTime || 0),
    orderStatus: Number(delivery?.orderStatus || 0),
  }
}

function pickFirstNonEmpty(values) {
  for (const value of values) {
    const normalized = String(value || '').trim()
    if (normalized) {
      return normalized
    }
  }

  return ''
}

function isPlainObject(value) {
  return Object.prototype.toString.call(value) === '[object Object]'
}
