import crypto from 'node:crypto'

import { runtimeConfig } from '../../../../config/runtime.js'
import { listTasksByOrderId, updateTask } from '../../../../repositories/task-repo.js'
import { createTaskEvent } from '../../../../repositories/task-event-repo.js'
import { getAgisoShopConfig } from '../shop-config-service.js'
import { queryAgisoXianyuOrderDetail } from './order-detail-service.js'
import { ensureAgisoXianyuAutoDeliveryMessageDeliveredForTask } from './message-service.js'
import { parseJsonObject } from '../../../../utils/json.js'
import { logWebhook } from '../../../../utils/logger.js'
import { nowIso } from '../../../../utils/time.js'
import type { TaskRow } from '../../../../types/repository-rows.js'

const AGISO_DUMMY_SEND_ENDPOINT = 'https://gw-api.agiso.com/aldsIdle/Order/DummySend'

type JsonObject = Record<string, unknown>

type AutoDeliveryOrder = {
  id?: number | string
  provider?: string
  platform?: string
  shop_id?: string
  shop_name?: string
  platform_order_id?: string
  [key: string]: unknown
}

type SupportedAgisoOrder = AutoDeliveryOrder & {
  id: number | string
  provider: 'agiso'
  platform: 'xianyu'
  platform_order_id: string
}

type AutoDeliveryTask = Partial<TaskRow> & {
  id?: number | string
  delivery_status?: string
  task_status?: string
  context_json?: string | JsonObject | null
  [key: string]: unknown
}

type AutoDeliveryTaskWithId = AutoDeliveryTask & {
  id: number | string
}

type EnsureAgisoXianyuAutoDeliveryInput = {
  order?: AutoDeliveryOrder | null
  task?: AutoDeliveryTask | null
  trigger?: string
}

type AutoDeliveryStatus = 'success' | 'failed' | 'skipped'

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

type FetchLike = (
  input: string | URL,
  init?: RequestInit,
) => Promise<FetchResponseLike>

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

type AgisoAutoDeliveryConfirmResult = {
  shipped: boolean
  orderStatus: number
  shipTime: number
  reason: string
}

type AutoDeliveryMessageResult = {
  sent: boolean
  skipped?: boolean
  reason?: string
  [key: string]: unknown
}

type EnsureAgisoXianyuAutoDeliveryDeps = {
  listTasksByOrderId?: (orderId: number | string) => Promise<AutoDeliveryTask[]>
  fetch?: FetchLike
  confirmAgisoXianyuAutoDeliveryShipped?: (
    input: AgisoAutoDeliveryConfirmInput,
  ) => Promise<AgisoAutoDeliveryConfirmResult>
  updateTask?: (
    taskId: number | string,
    patch: { context_json: string; updated_at: string },
  ) => Promise<AutoDeliveryTask | null>
  createTaskEvent?: (
    taskId: number | string,
    eventType: string,
    payload?: unknown,
    createdAt?: string,
  ) => Promise<unknown>
  nowIso?: () => string
  ensureAgisoXianyuAutoDeliveryMessageDeliveredForTask?: (
    input: { order?: AutoDeliveryOrder | null; task?: AutoDeliveryTask | null },
  ) => Promise<AutoDeliveryMessageResult>
}

type PersistAgisoAutoDeliveryResultInput = {
  status: AutoDeliveryStatus
  trigger?: string
  reason?: string
  order?: AutoDeliveryOrder | null
  responseStatus?: number
  response?: unknown
  errorMessage?: string
  detail?: JsonObject
}

type AgisoAutoDeliveryConfig = {
  enabled: boolean
  endpoint: string
  apiVersion: string
  appSecret: string
  accessToken: string
}

export async function ensureAgisoXianyuAutoDeliveryForDeliveredTask({
  order,
  task,
  trigger = 'task_delivered',
}: EnsureAgisoXianyuAutoDeliveryInput = {}) {
  return ensureAgisoXianyuAutoDeliveryForDeliveredTaskWithDeps({ order, task, trigger })
}

export async function ensureAgisoXianyuAutoDeliveryForDeliveredTaskWithDeps({
  order,
  task,
  trigger = 'task_delivered',
}: EnsureAgisoXianyuAutoDeliveryInput = {}, deps: EnsureAgisoXianyuAutoDeliveryDeps = {}) {
  const listOrderTasks = deps.listTasksByOrderId || listTasksByOrderId
  const sendRequest = deps.fetch || (fetch as FetchLike)
  const confirmShipped = deps.confirmAgisoXianyuAutoDeliveryShipped || confirmAgisoXianyuAutoDeliveryShipped

  if (!isAgisoXianyuOrder(order) || !task?.id) {
    return { sent: false, skipped: true, reason: 'not_supported', task }
  }

  const currentTask = task as AutoDeliveryTaskWithId
  if (String(currentTask.delivery_status || '').trim() !== 'delivered') {
    return { sent: false, skipped: true, reason: 'task_not_delivered', task: currentTask }
  }

  const orderTasks = await listOrderTasks(order.id)
  if (hasAgisoAutoDeliverySucceeded(orderTasks)) {
    return { sent: false, skipped: true, reason: 'already_sent', task: currentTask }
  }

  if (!isOrderReadyForAgisoAutoDelivery(orderTasks)) {
    logWebhook('[agiso/xianyu/auto-delivery]', '跳过自动发货：订单下仍有任务未完成交付', {
      trigger,
      orderId: order.id,
      taskId: currentTask.id,
      platformOrderId: order.platform_order_id,
      taskStatuses: orderTasks.map((current) => ({
        taskId: current.id,
        taskStatus: current.task_status,
        deliveryStatus: current.delivery_status,
      })),
    })

    return { sent: false, skipped: true, reason: 'waiting_other_tasks', task: currentTask }
  }

  const config = resolveAgisoXianyuAutoDeliveryConfig(order)
  if (!config.enabled) {
    return persistAgisoAutoDeliveryResult(currentTask, {
      status: 'skipped',
      trigger,
      reason: 'auto_delivery_disabled',
      order,
    }, deps)
  }

  if (!config.endpoint || !config.accessToken || !config.appSecret) {
    return persistAgisoAutoDeliveryResult(currentTask, {
      status: 'skipped',
      trigger,
      reason: 'missing_config',
      order,
      detail: {
        hasEndpoint: Boolean(config.endpoint),
        hasAccessToken: Boolean(config.accessToken),
        hasAppSecret: Boolean(config.appSecret),
      },
    }, deps)
  }

  const requestHeaders = buildRequestHeaders({
    accessToken: config.accessToken,
    apiVersion: config.apiVersion,
  })
  const requestBody = buildRequestBody({
    platformOrderId: String(order.platform_order_id || '').trim(),
    appSecret: config.appSecret,
  })

  logWebhook('[agiso/xianyu/auto-delivery]', '开始执行 Agiso 咸鱼自动发货', {
    trigger,
    orderId: order.id,
    taskId: currentTask.id,
    shopId: order.shop_id,
    platformOrderId: order.platform_order_id,
    endpoint: config.endpoint,
  })

  try {
    const response = await sendRequest(config.endpoint, {
      method: 'POST',
      headers: requestHeaders,
      body: new URLSearchParams(requestBody).toString(),
    })
    const rawText = await response.text()
    const parsed = parseJsonObject(rawText) as JsonObject
    const success = isAgisoAutoDeliverySuccess(response.status, parsed)

    if (success) {
      const requestId = String(parsed?.RequestId || '').trim()

      // 发货接口返回成功后，再查一次 Order/Detail 确认订单真的进入已发货状态。
      const confirmResult = await confirmShipped({
        shopId: order.shop_id,
        platformOrderId: order.platform_order_id,
        requestId,
      })

      if (!confirmResult.shipped) {
        logWebhook('[agiso/xianyu/auto-delivery]', 'Agiso 咸鱼自动发货接口返回成功，但订单状态未确认进入已发货', {
          trigger,
          orderId: order.id,
          taskId: currentTask.id,
          shopId: order.shop_id,
          platformOrderId: order.platform_order_id,
          responseStatus: response.status,
          requestId,
          confirmOrderStatus: confirmResult.orderStatus,
          confirmShipTime: confirmResult.shipTime,
          confirmReason: confirmResult.reason,
        }, { level: 'warn' })

        return persistAgisoAutoDeliveryResult(currentTask, {
          status: 'failed',
          trigger,
          reason: 'delivery_not_confirmed',
          order,
          responseStatus: response.status,
          response: parsed,
          errorMessage: `Agiso 发货状态更新接口已受理，但订单仍未进入已发货，请检查该订单是否允许无物流发货 (orderStatus=${confirmResult.orderStatus}, shipTime=${confirmResult.shipTime})`,
          detail: {
            requestId,
            confirmOrderStatus: confirmResult.orderStatus,
            confirmShipTime: confirmResult.shipTime,
          },
        }, deps)
      }

      logWebhook('[agiso/xianyu/auto-delivery]', 'Agiso 咸鱼自动发货成功，订单已确认进入已发货状态', {
        trigger,
        orderId: order.id,
        taskId: currentTask.id,
        shopId: order.shop_id,
        platformOrderId: order.platform_order_id,
        responseStatus: response.status,
        requestId,
        confirmOrderStatus: confirmResult.orderStatus,
        confirmShipTime: confirmResult.shipTime,
      })

      const result = await persistAgisoAutoDeliveryResult(currentTask, {
        status: 'success',
        trigger,
        reason: '',
        order,
        responseStatus: response.status,
        response: parsed,
        detail: {
          requestId,
          confirmOrderStatus: confirmResult.orderStatus,
          confirmShipTime: confirmResult.shipTime,
        },
      }, deps)

      // 发货确认成功后，发送自定义消息通知。
      await sendAutoDeliveryMessage({ order, task: currentTask }, deps)

      return result
    }

    const errorMessage = resolveAgisoAutoDeliveryErrorMessage(parsed, rawText, response.status)
    logWebhook('[agiso/xianyu/auto-delivery]', 'Agiso 咸鱼自动发货失败', {
      trigger,
      orderId: order.id,
      taskId: currentTask.id,
      shopId: order.shop_id,
      platformOrderId: order.platform_order_id,
      responseStatus: response.status,
      errorMessage,
      response: parsed,
    }, { level: 'warn' })

    return persistAgisoAutoDeliveryResult(currentTask, {
      status: 'failed',
      trigger,
      reason: 'request_failed',
      order,
      responseStatus: response.status,
      response: parsed,
      errorMessage,
    }, deps)
  } catch (error) {
    const errorMessage = error instanceof Error ? error.message : String(error || 'Agiso 咸鱼自动发货失败')
    logWebhook('[agiso/xianyu/auto-delivery]', 'Agiso 咸鱼自动发货异常', {
      trigger,
      orderId: order.id,
      taskId: currentTask.id,
      shopId: order.shop_id,
      platformOrderId: order.platform_order_id,
      errorMessage,
    }, { level: 'warn' })

    return persistAgisoAutoDeliveryResult(currentTask, {
      status: 'failed',
      trigger,
      reason: 'request_error',
      order,
      responseStatus: 0,
      response: {},
      errorMessage,
    }, deps)
  }
}

export function hasAgisoAutoDeliverySucceeded(tasks: AutoDeliveryTask[] = []): boolean {
  return (Array.isArray(tasks) ? tasks : []).some((task) => {
    const context = parseTaskContext(task)
    const autoDelivery = isPlainObject(context.agisoAutoDelivery) ? context.agisoAutoDelivery : {}
    return String(autoDelivery.status || '').trim() === 'success'
  })
}

export function isOrderReadyForAgisoAutoDelivery(tasks: AutoDeliveryTask[] = []): boolean {
  const normalizedTasks = Array.isArray(tasks) ? tasks.filter(Boolean) : []
  return normalizedTasks.length > 0
    && normalizedTasks.every((task) => String(task.delivery_status || '').trim() === 'delivered')
}

function resolveAgisoXianyuAutoDeliveryConfig(order: AutoDeliveryOrder): AgisoAutoDeliveryConfig {
  const baseConfig = runtimeConfig.platforms.agiso.autoDelivery
  const shopConfig = getAgisoShopConfig(String(order?.shop_id || '').trim()) || {}

  return {
    enabled: normalizeBooleanLike(baseConfig.enabled, true),
    endpoint: resolveAgisoAutoDeliveryEndpoint(baseConfig.endpoint),
    apiVersion: String(baseConfig.apiVersion || shopConfig.apiVersion || '1').trim() || '1',
    appSecret: String(shopConfig.appSecret || runtimeConfig.platforms?.agiso?.appSecret || '').trim(),
    accessToken: String(shopConfig.accessToken || '').trim(),
  }
}

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

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

  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 = String(appSecret || '').trim()

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

  raw += String(appSecret || '').trim()

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

export function isAgisoAutoDeliverySuccess(statusCode: number, payload: unknown): boolean {
  if (statusCode < 200 || statusCode >= 300) {
    return false
  }

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

  const normalizedPayload = payload as JsonObject
  if (normalizedPayload.IsSuccess === true) {
    return true
  }

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

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

  return false
}

export function resolveAgisoAutoDeliveryErrorMessage(
  payload: unknown,
  rawText: string,
  statusCode: number,
): string {
  if (payload && typeof payload === 'object') {
    const normalizedPayload = payload as JsonObject
    for (const value of [
      normalizedPayload.Error_Msg,
      normalizedPayload.msg,
      normalizedPayload.message,
      normalizedPayload.error,
    ]) {
      const normalized = String(value || '').trim()
      if (normalized) {
        return normalized
      }
    }
  }

  const text = String(rawText || '').trim()
  return text || `Agiso 咸鱼自动发货失败，HTTP ${statusCode}`
}

export function resolveAgisoAutoDeliveryEndpoint(value: unknown): string {
  return AGISO_DUMMY_SEND_ENDPOINT
}

async function persistAgisoAutoDeliveryResult(task: AutoDeliveryTaskWithId, {
  status,
  trigger,
  reason,
  order,
  responseStatus = 0,
  response = {},
  errorMessage = '',
  detail = {},
}: PersistAgisoAutoDeliveryResultInput, deps: EnsureAgisoXianyuAutoDeliveryDeps = {}) {
  const getNowIso = deps.nowIso || nowIso
  const patchTask = deps.updateTask || updateTask
  const insertTaskEvent = deps.createTaskEvent || createTaskEvent
  const now = getNowIso()
  const currentContext = parseTaskContext(task)
  const nextContext = {
    ...currentContext,
    agisoAutoDelivery: {
      status,
      trigger: String(trigger || '').trim(),
      reason: String(reason || '').trim(),
      platformOrderId: String(order?.platform_order_id || '').trim(),
      responseStatus: Number(responseStatus || 0),
      errorMessage: String(errorMessage || '').trim(),
      response: isPlainObject(response) ? response : {},
      updatedAt: now,
      ...detail,
    },
  }
  const updatedTask = await patchTask(task.id, {
    context_json: JSON.stringify(nextContext),
    updated_at: now,
  })

  if (status === 'success' || status === 'failed' || status === 'skipped') {
    await insertTaskEvent(task.id, `agiso_auto_delivery_${status}`, {
      trigger: String(trigger || '').trim(),
      reason: String(reason || '').trim(),
      platformOrderId: String(order?.platform_order_id || '').trim(),
      responseStatus: Number(responseStatus || 0),
      errorMessage: String(errorMessage || '').trim(),
      ...detail,
    }, now)
  }

  return {
    sent: status === 'success',
    skipped: status === 'skipped',
    reason: String(reason || '').trim(),
    errorMessage: String(errorMessage || '').trim(),
    responseStatus: Number(responseStatus || 0),
    response,
    task: updatedTask || task,
  }
}

function parseTaskContext(task: AutoDeliveryTask | null | undefined): JsonObject {
  const value = task?.context_json

  if (!value) {
    return {}
  }

  if (isPlainObject(value)) {
    return value
  }

  try {
    return JSON.parse(String(value || '{}'))
  } catch {
    return {}
  }
}

function isAgisoXianyuOrder(order: AutoDeliveryOrder | null | undefined): order is SupportedAgisoOrder {
  return String(order?.provider || '').trim() === 'agiso'
    && String(order?.platform || '').trim() === 'xianyu'
    && Boolean(String(order?.platform_order_id || '').trim())
    && Number(order?.id || 0) > 0
}

function normalizeBooleanLike(value: unknown, fallbackValue: boolean): boolean {
  if (typeof value === 'boolean') {
    return value
  }

  if (typeof value === 'undefined' || value === null || value === '') {
    return fallbackValue
  }

  const normalized = String(value || '').trim().toLowerCase()
  if (['1', 'true', 'yes', 'on'].includes(normalized)) {
    return true
  }

  if (['0', 'false', 'no', 'off'].includes(normalized)) {
    return false
  }

  return fallbackValue
}

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

/**
 * 发货接口返回成功后，再查一次 Order/Detail 确认订单是否真的进入已发货状态。
 * 只有 ship_time > 0 或 orderStatus >= 3 才算发货确认通过
 */
async function confirmAgisoXianyuAutoDeliveryShipped({
  shopId = '',
  platformOrderId = '',
  requestId = '',
}: AgisoAutoDeliveryConfirmInput = {}): Promise<AgisoAutoDeliveryConfirmResult> {
  try {
    const detailResult = await queryAgisoXianyuOrderDetail({ shopId, platformOrderId, requestId })

    if (detailResult.success && detailResult.shipped) {
      return {
        shipped: true,
        orderStatus: detailResult.orderStatus,
        shipTime: detailResult.shipTime,
        reason: '',
      }
    }

    return {
      shipped: false,
      orderStatus: detailResult.orderStatus || 0,
      shipTime: detailResult.shipTime || 0,
      reason: detailResult.success ? 'order_not_shipped' : detailResult.reason,
    }
  } catch (error) {
    const message = error instanceof Error ? error.message : String(error || '订单发货状态确认失败')
    logWebhook('[agiso/xianyu/auto-delivery]', '自动发货确认查询异常', {
      requestId,
      shopId,
      platformOrderId,
      errorMessage: message,
    }, { level: 'warn' })

    return {
      shipped: false,
      orderStatus: 0,
      shipTime: 0,
      reason: 'confirm_query_failed',
    }
  }
}

/**
 * 自动发货成功后发送消息通知。
 */
async function sendAutoDeliveryMessage(
  { order, task }: { order?: AutoDeliveryOrder | null; task?: AutoDeliveryTask | null } = {},
  deps: EnsureAgisoXianyuAutoDeliveryDeps = {},
): Promise<AutoDeliveryMessageResult> {
  try {
    const deliverMessage = deps.ensureAgisoXianyuAutoDeliveryMessageDeliveredForTask
      || ensureAgisoXianyuAutoDeliveryMessageDeliveredForTask
    const result = await deliverMessage({ order, task })
    logWebhook('[agiso/xianyu/auto-delivery]', '自动发货消息通知结果', {
      orderId: order?.id,
      taskId: task?.id,
      platformOrderId: order?.platform_order_id,
      sent: result.sent,
      skipped: result.skipped,
      reason: result.reason,
    })
    return result
  } catch (error) {
    const message = error instanceof Error ? error.message : String(error || '发送自动发货消息通知失败')
    logWebhook('[agiso/xianyu/auto-delivery]', '自动发货消息通知异常', {
      orderId: order?.id,
      taskId: task?.id,
      platformOrderId: order?.platform_order_id,
      errorMessage: message,
    }, { level: 'warn' })

    return { sent: false, skipped: true, reason: 'message_send_error' }
  }
}
