#!/usr/bin/env node

import crypto from 'node:crypto'
import process from 'node:process'
import { fileURLToPath } from 'node:url'
import { setTimeout as sleep } from 'node:timers/promises'

import { query, closeDb } from '../src/db/client.js'
import { parseJsonObject } from '../src/utils/json.js'
import { getAgisoShopConfig } from '../src/services/platforms/agiso/shop-config-service.js'
import { queryAgisoXianyuOrderDetail } from '../src/services/platforms/agiso/xianyu/order-detail-service.js'

const DEFAULT_DUMMY_ENDPOINT = 'https://gw-api.agiso.com/aldsIdle/Order/DummySend'
const DEFAULT_POLL_SECONDS = [0, 3, 10]

async function main() {
  const options = parseArgs(process.argv.slice(2))
  if (options.help || !options.orderId) {
    printHelp()
    process.exit(options.help ? 0 : 1)
  }

  const orderRecord = await resolveOrderRecord(options)
  const shopId = orderRecord?.shop_id || options.shopId
  const shopConfig = getAgisoShopConfig(shopId) || {}
  const accessToken = String(shopConfig.accessToken || '').trim()
  const appSecret = String(shopConfig.appSecret || process.env.AGISO_APP_SECRET || '').trim()

  if (!shopId) {
    throw new Error('未提供 shopId，且数据库中也没有查到该订单对应的店铺')
  }

  if (!accessToken) {
    throw new Error(`店铺 ${shopId} 缺少 accessToken 配置，请检查 apps/backend/data/agiso-shops.json`)
  }

  if (!appSecret) {
    throw new Error('缺少 AGISO_APP_SECRET 或店铺级 appSecret 配置')
  }

  printJson('测试输入', {
    orderId: options.orderId,
    shopId,
    pollSeconds: options.pollSeconds,
    orderRecord: orderRecord
      ? {
        id: Number(orderRecord.id || 0) || null,
        platformOrderId: String(orderRecord.platform_order_id || '').trim(),
        orderStatus: String(orderRecord.order_status || '').trim(),
        payStatus: String(orderRecord.pay_status || '').trim(),
        provider: String(orderRecord.provider || '').trim(),
        platform: String(orderRecord.platform || '').trim(),
      }
      : null,
  })

  const beforeDetail = await queryAgisoXianyuOrderDetail({
    shopId,
    platformOrderId: options.orderId,
    requestId: `diag-before-${Date.now()}`,
  })
  printJson('调用前订单详情', summarizeDetail(beforeDetail))

  const result = await executeRequest({
    platformOrderId: options.orderId,
    accessToken,
    appSecret,
  })
  printJson('接口响应 更新发货状态 DummySend', result)

  for (const seconds of options.pollSeconds) {
    if (seconds > 0) {
      await sleep(seconds * 1000)
    }

    const detail = await queryAgisoXianyuOrderDetail({
      shopId,
      platformOrderId: options.orderId,
      requestId: `diag-after-${seconds}s-${Date.now()}`,
    })
    printJson(`调用后订单详情 +${seconds}s`, summarizeDetail(detail))
  }
}

function parseArgs(argv) {
  const output = {
    orderId: '',
    shopId: '',
    pollSeconds: [...DEFAULT_POLL_SECONDS],
    help: false,
  }

  for (let index = 0; index < argv.length; index += 1) {
    const current = String(argv[index] || '').trim()
    if (!current) {
      continue
    }

    if (!current.startsWith('--')) {
      if (!output.orderId) {
        output.orderId = current
        continue
      }

      throw new Error(`无法识别的参数：${current}`)
    }

    if (current === '--help') {
      output.help = true
      continue
    }

    const [rawKey, inlineValue = ''] = current.split('=', 2)
    const key = rawKey.slice(2)
    const nextValue = inlineValue || argv[index + 1] || ''
    const shouldConsumeNext = !inlineValue && argv[index + 1] && !String(argv[index + 1]).startsWith('--')

    switch (key) {
      case 'shop-id':
        output.shopId = String(nextValue || '').trim()
        break
      case 'poll':
        output.pollSeconds = parsePollSeconds(nextValue)
        break
      default:
        throw new Error(`无法识别的参数：${current}`)
    }

    if (shouldConsumeNext) {
      index += 1
    }
  }

  return output
}

function parsePollSeconds(value) {
  const normalized = String(value || '').trim()
  if (!normalized) {
    return [...DEFAULT_POLL_SECONDS]
  }

  const parsed = normalized
    .split(',')
    .map((item) => Number(String(item || '').trim()))
    .filter((item) => Number.isFinite(item) && item >= 0)
    .map((item) => Math.floor(item))

  if (parsed.length === 0) {
    throw new Error(`poll 参数格式无效：${value}`)
  }

  return parsed
}

async function resolveOrderRecord(options) {
  const explicitShopId = String(options.shopId || '').trim()
  if (explicitShopId) {
    const result = await query(
      `
        SELECT *
        FROM orders
        WHERE provider = 'agiso'
          AND platform = 'xianyu'
          AND shop_id = $1
          AND platform_order_id = $2
        ORDER BY id DESC
        LIMIT 1
      `,
      [explicitShopId, options.orderId],
    )

    return result.rows[0] || null
  }

  const result = await query(
    `
      SELECT *
      FROM orders
      WHERE provider = 'agiso'
        AND platform = 'xianyu'
        AND platform_order_id = $1
      ORDER BY id DESC
      LIMIT 2
    `,
    [options.orderId],
  )

  if (result.rows.length > 1) {
    throw new Error(`数据库里命中多条同订单号记录，请显式传入 --shop-id。订单号：${options.orderId}`)
  }

  return result.rows[0] || null
}

async function executeRequest(context) {
  const timestamp = String(Math.floor(Date.now() / 1000))
  const requestBody = buildDummySendBody(context, timestamp)
  const response = await fetch(DEFAULT_DUMMY_ENDPOINT, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${context.accessToken}`,
      ApiVersion: '1',
      'content-type': 'application/x-www-form-urlencoded; charset=utf-8',
    },
    body: new URLSearchParams(requestBody).toString(),
  })
  const rawText = await response.text()
  const payload = parseJsonObject(rawText, { preserveLargeIntegers: true })

  return {
    endpoint: DEFAULT_DUMMY_ENDPOINT,
    status: response.status,
    requestBody,
    response: payload,
    rawText,
  }
}

function buildDummySendBody(context, timestamp) {
  const payload = {
    tid: context.platformOrderId,
    timestamp,
  }

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

function generateSign(params, appSecret) {
  let raw = String(appSecret || '').trim()
  for (const [key, value] of Object.entries(params).sort(([left], [right]) => left.localeCompare(right))) {
    raw += `${key}${value}`
  }
  raw += String(appSecret || '').trim()
  return crypto.createHash('md5').update(raw, 'utf8').digest('hex').toLowerCase()
}

function summarizeDetail(detailResult) {
  return {
    success: Boolean(detailResult?.success),
    reason: String(detailResult?.reason || '').trim(),
    errorMessage: String(detailResult?.errorMessage || '').trim(),
    responseStatus: Number(detailResult?.responseStatus || 0),
    shipped: Boolean(detailResult?.shipped),
    shipTime: Number(detailResult?.shipTime || 0),
    orderStatus: Number(detailResult?.orderStatus || 0),
    bizOrderId: String(detailResult?.detailPayload?.biz_order_id || '').trim(),
    sellerNick: String(detailResult?.detailPayload?.seller_nick || '').trim(),
    buyerNick: String(detailResult?.detailPayload?.buyer_nick || '').trim(),
    sku: String(detailResult?.detailPayload?.sku || '').trim(),
    itemTitle: String(detailResult?.detailPayload?.item?.title || '').trim(),
  }
}

function printJson(title, value) {
  process.stdout.write(`\n=== ${title} ===\n`)
  process.stdout.write(`${JSON.stringify(value, null, 2)}\n`)
}

function printHelp() {
  process.stdout.write(`用法:
  npm run agiso:auto-delivery:test -- <订单号> [--shop-id <店铺ID>] [--poll 0,3,10]

说明:
  1. 该脚本只测试正确的发货接口 DummySend。
  2. 会先查 Order/Detail，再调用 DummySend，再按 poll 秒数轮询发货状态。

示例:
  npm run agiso:auto-delivery:test -- 4502285714045005830
  npm run agiso:auto-delivery:test -- 4502285714045005830 --poll 0,5,15
  npm run agiso:auto-delivery:test -- 4502285714045005830 --shop-id 2209880145223
`)
}

const isNodeTestRunner = Array.isArray(process.execArgv) && process.execArgv.includes('--test')
const isDirectRun = !isNodeTestRunner && process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]

if (isDirectRun) {
  main()
    .catch((error) => {
      process.stderr.write(`${error instanceof Error ? error.stack || error.message : String(error)}\n`)
      process.exitCode = 1
    })
    .finally(async () => {
      await closeDb()
    })
}
