import { createTaskEvent } from '../../../repositories/task-event-repo.js'
import { getTaskById, updateTask } from '../../../repositories/task-repo.js'
import { asJsonObject, type JsonObject } from '../../../types/json.js'
import {
  findKuaishouIndustryVoucherByCode,
  listKuaishouIndustryVouchersByTaskId,
  updateKuaishouIndustryVoucherByCode,
} from '../../../repositories/kuaishou-industry-voucher-repo.js'
import { TASK_STATUS } from '../../../domain/task-status.js'
import { logWarn } from '../../../utils/logger.js'
import { normalizeTimestampIso } from '../../../utils/time.js'
import { buildIndustryEticketItem } from './response.js'
import { consumeCallback } from './consume-callback-service.js'
import { destroyCallback } from './destroy-callback-service.js'
import type { KuaishouIndustryVoucherRow, TaskRow } from '../../../types/repository/rows.js'

export const KUAISHOU_INDUSTRY_DEFAULT_VALID_DAYS = 30
export const KUAISHOU_INDUSTRY_SEND_CALLBACK_STATUS = {
  PENDING: 'pending',
  SUCCESS: 'success',
  FAILED: 'failed',
} as const
export const KUAISHOU_INDUSTRY_DEFAULT_CONSUME_TYPE = 'consume'
export const KUAISHOU_INDUSTRY_DESTROY_REASONS = {
  ETICKET_EXPIRED: 'ETICKET_EXPIRED',
  USER_APPLY_REFUND: 'USER_APPLY_REFUND',
  SUPPLY_DESTROY: 'SUPPLY_DESTROY',
  SYS_ADMIN_DESTROY: 'SYS_ADMIN_DESTROY',
} as const

export function resolveKuaishouIndustryVoucherValidity(
  input: {
    certActualStartTime?: unknown
    certStartTime?: unknown
    certActualEndTime?: unknown
    certEndTime?: unknown
    certExpDays?: unknown
  } = {},
  nowMs = Date.now(),
) {
  const certActualStartTime = normalizePositiveTimestamp(input.certActualStartTime)
  const certStartTime = normalizePositiveTimestamp(input.certStartTime)
  const certActualEndTime = normalizePositiveTimestamp(input.certActualEndTime)
  const certEndTime = normalizePositiveTimestamp(input.certEndTime)
  const certExpDays = normalizePositiveInteger(
    input.certExpDays,
    KUAISHOU_INDUSTRY_DEFAULT_VALID_DAYS,
  )
  const durationMs = certExpDays * 86_400_000
  const validStartTime = certActualStartTime || certStartTime || normalizePositiveTimestamp(nowMs)
  const validEndTime =
    certActualEndTime ||
    certEndTime ||
    (certStartTime ? certStartTime + durationMs : validStartTime + durationMs)

  return {
    validStartTime,
    validEndTime,
    certExpDays,
  }
}

/**
 * 解析电子凭证类型。优先级：
 * 显式入参 > voucher.eticket_type > raw_payload.body.eticketType > raw_payload.eticketType
 */
export function resolveKuaishouIndustryEticketType(
  voucher?: Pick<KuaishouIndustryVoucherRow, 'eticket_type' | 'raw_payload_json'> | null,
  explicit?: unknown,
): string {
  const fromInput = String(explicit || '').trim()
  if (fromInput) {
    return fromInput
  }

  if (!voucher) {
    return ''
  }

  const fromColumn = String(voucher.eticket_type || '').trim()
  if (fromColumn) {
    return fromColumn
  }

  const rawPayload = parseJsonObject(voucher.raw_payload_json)
  const body = parseJsonObject(rawPayload.body)
  return String(body.eticketType || rawPayload.eticketType || '').trim()
}

export function buildKuaishouIndustryEticketFromVoucher(
  voucher: KuaishouIndustryVoucherRow,
  eticketType = '',
) {
  return buildIndustryEticketItem({
    id: String(voucher.voucher_code || ''),
    code: String(voucher.voucher_code || ''),
    status: normalizeVoucherStatus(voucher.status),
    num: 1,
    validStartTime: Number(voucher.valid_start_time || 0) || 0,
    validEndTime: Number(voucher.valid_end_time || 0) || 0,
    eticketType: resolveKuaishouIndustryEticketType(voucher, eticketType),
    consumeDetails: resolveVoucherConsumeDetails(voucher),
  })
}

export function normalizeKuaishouIndustrySendCallbackStatus(value: unknown) {
  const normalized = String(value || '')
    .trim()
    .toLowerCase()
  if (
    normalized === KUAISHOU_INDUSTRY_SEND_CALLBACK_STATUS.PENDING ||
    normalized === KUAISHOU_INDUSTRY_SEND_CALLBACK_STATUS.FAILED
  ) {
    return normalized
  }

  return KUAISHOU_INDUSTRY_SEND_CALLBACK_STATUS.SUCCESS
}

export function isKuaishouIndustryVoucherSendCallbackSuccess(
  voucher: Pick<KuaishouIndustryVoucherRow, 'send_callback_status'>,
) {
  return (
    normalizeKuaishouIndustrySendCallbackStatus(voucher.send_callback_status) ===
    KUAISHOU_INDUSTRY_SEND_CALLBACK_STATUS.SUCCESS
  )
}

export function resolveKuaishouIndustryVoucherSendCallbackMessage(
  voucher: Pick<KuaishouIndustryVoucherRow, 'send_callback_status' | 'send_callback_last_error'>,
) {
  const status = normalizeKuaishouIndustrySendCallbackStatus(voucher.send_callback_status)
  if (status === KUAISHOU_INDUSTRY_SEND_CALLBACK_STATUS.SUCCESS) {
    return ''
  }

  const errorMessage = String(voucher.send_callback_last_error || '').trim()
  if (errorMessage) {
    return errorMessage
  }

  return status === KUAISHOU_INDUSTRY_SEND_CALLBACK_STATUS.PENDING
    ? '电子凭证发码回调尚未成功，暂不能继续履约'
    : '电子凭证发码回调失败，暂不能继续履约'
}

export async function consumeKuaishouIndustryVouchersForTask(
  task: TaskRow,
  input: {
    source?: string
    token?: string
    consumeType?: string
    consumeTime?: number
    storeName?: string
    storeAddress?: string
    expressCode?: string
    expressNo?: string
  } = {},
) {
  const vouchers = await resolveTaskVouchers(task)
  const consumed: KuaishouIndustryVoucherRow[] = []
  const failed: Array<{ voucher: KuaishouIndustryVoucherRow; errorMessage: string }> = []

  for (const voucher of vouchers) {
    const consumeInput: Parameters<typeof consumeKuaishouIndustryVoucher>[1] = {
      source: input.source || 'fulfillment_completed',
      consumeType: normalizeKuaishouIndustryConsumeType(input.consumeType),
      consumeTime: input.consumeTime || Date.now(),
      task,
    }
    if (input.token !== undefined) consumeInput.token = input.token
    if (input.storeName !== undefined) consumeInput.storeName = input.storeName
    if (input.storeAddress !== undefined) consumeInput.storeAddress = input.storeAddress
    if (input.expressCode !== undefined) consumeInput.expressCode = input.expressCode
    if (input.expressNo !== undefined) consumeInput.expressNo = input.expressNo

    const result = await consumeKuaishouIndustryVoucher(voucher, consumeInput)

    if (result.ok && result.voucher) {
      consumed.push(result.voucher)
      continue
    }

    failed.push({
      voucher,
      errorMessage: result.errorMessage || '电子凭证核销回调失败',
    })
  }

  return {
    vouchers,
    consumed,
    failed,
    ok: vouchers.length > 0 && failed.length === 0,
  }
}

export async function consumeKuaishouIndustryVoucher(
  voucher: KuaishouIndustryVoucherRow,
  input: {
    source?: string
    token?: string
    consumeType?: string
    consumeTime?: number
    storeName?: string
    storeAddress?: string
    expressCode?: string
    expressNo?: string
    task?: TaskRow
    serialNum?: string
    skipCallback?: boolean
  } = {},
) {
  const currentStatus = normalizeVoucherStatus(voucher.status)
  if (currentStatus === 'CONSUMED') {
    return {
      ok: true,
      voucher,
      callbackSuccess: true,
    }
  }

  if (currentStatus === 'DESTROYED') {
    return {
      ok: false,
      voucher,
      callbackSuccess: false,
      errorMessage: '电子凭证已销毁，不能核销',
    }
  }

  if (!isKuaishouIndustryVoucherSendCallbackSuccess(voucher)) {
    return {
      ok: false,
      voucher,
      callbackSuccess: false,
      errorMessage: resolveKuaishouIndustryVoucherSendCallbackMessage(voucher),
    }
  }

  const now = new Date().toISOString()
  const consumeTime = Number(input.consumeTime || Date.now()) || Date.now()
  const consumeType = normalizeKuaishouIndustryConsumeType(input.consumeType)
  const serialNum = String(
    input.serialNum || voucher.consume_serial_num || `CONSUME-${voucher.voucher_code}`,
  ).trim()
  const token = String(input.token || voucher.token || '').trim()
  const consumeDetail = {
    serialNum,
    consumeType,
    consumeTime,
    storeName: input.storeName || undefined,
    storeAddress: input.storeAddress || undefined,
    expressCode: input.expressCode || undefined,
    expressNo: input.expressNo || undefined,
    source: input.source || 'system',
  }

  const callbackResult = input.skipCallback
    ? { success: true as const }
    : await consumeCallback({
        oid: voucher.oid,
        sellerId: String(voucher.seller_id || '').trim(),
        etickets: [
          {
            id: voucher.voucher_code,
            num: 1,
          },
        ],
        status: 'CONSUMED',
        consumeType,
        consumeTime,
        token,
        seriallNum: serialNum,
        ...(input.storeName ? { storeName: input.storeName } : {}),
        ...(input.storeAddress ? { storeAddress: input.storeAddress } : {}),
        ...(input.expressCode ? { expressCode: input.expressCode } : {}),
        ...(input.expressNo ? { expressNo: input.expressNo } : {}),
      })

  if (!callbackResult.success) {
    return {
      ok: false,
      voucher,
      callbackSuccess: false,
      errorMessage: callbackResult.error || '电子凭证核销回调失败',
    }
  }

  const nextDetails = appendConsumeDetail(resolveVoucherConsumeDetails(voucher), consumeDetail)
  const updated = await updateKuaishouIndustryVoucherByCode(voucher.voucher_code, {
    status: 'CONSUMED',
    consumeSerialNum: serialNum,
    consumeDetailsJson: nextDetails,
    consumedAt: now,
    updatedAt: now,
  })

  if (input.task) {
    await createTaskEvent(
      input.task.id,
      'kuaishou_industry_voucher_consumed',
      {
        oid: voucher.oid,
        voucherCode: voucher.voucher_code,
        serialNum,
        consumeType,
        consumeTime,
        source: input.source || 'system',
      },
      now,
    )
  }

  return {
    ok: true,
    voucher: updated || voucher,
    callbackSuccess: true,
  }
}

/**
 * 主动销毁电子凭证（管理端/运维补救）。
 * 先调快手销毁回调，成功后再落本地 DESTROYED 并关闭关联任务。
 * 已 DESTROYED 时仅重试回调（幂等）。
 */
export async function destroyKuaishouIndustryVoucher(
  voucher: KuaishouIndustryVoucherRow,
  input: {
    source?: string
    reason?: string
    token?: string
    eticketType?: string
    task?: TaskRow | null
    skipCallback?: boolean
    goodsValue?: number
  } = {},
) {
  const currentStatus = normalizeVoucherStatus(voucher.status)
  if (currentStatus === 'CONSUMED') {
    return {
      ok: false,
      voucher,
      callbackSuccess: false,
      errorMessage: '电子凭证已核销，不能销毁；如需作废请先冲正',
    }
  }

  const reason = normalizeKuaishouIndustryDestroyReason(input.reason)
  const now = normalizeTimestampIso(new Date().toISOString())
  const token = String(input.token || voucher.token || '').trim()
  const eticketType = resolveKuaishouIndustryEticketType(voucher, input.eticketType)
  const alreadyDestroyed = currentStatus === 'DESTROYED'

  const callbackResult = input.skipCallback
    ? { success: true as const }
    : await destroyCallback({
        oid: voucher.oid,
        sellerId: String(voucher.seller_id || '').trim(),
        etickets: [
          {
            id: voucher.voucher_code,
            code: voucher.voucher_code,
            num: 1,
            ...(input.goodsValue != null ? { goodsValue: input.goodsValue } : {}),
          },
        ],
        reason,
        token,
        ...(eticketType ? { eticketType } : {}),
      })

  if (!callbackResult.success) {
    return {
      ok: false,
      voucher,
      callbackSuccess: false,
      errorMessage: callbackResult.error || '电子凭证销毁回调失败',
    }
  }

  let updated = voucher
  if (!alreadyDestroyed) {
    const next = await updateKuaishouIndustryVoucherByCode(voucher.voucher_code, {
      status: 'DESTROYED',
      destroyedAt: now,
      updatedAt: now,
      ...(eticketType && !String(voucher.eticket_type || '').trim() ? { eticketType } : {}),
    })
    updated = next || voucher
  }

  let task =
    input.task || (voucher.task_id ? await getTaskById(voucher.task_id).catch(() => null) : null)

  if (task && !alreadyDestroyed) {
    task =
      (await updateTask(task.id, {
        task_status: TASK_STATUS.CLOSED,
        delivery_status: 'cancelled',
        result_code: reason,
        result_message: `电子凭证销毁: ${reason}`,
        updated_at: now,
      })) || task

    await createTaskEvent(
      task.id,
      'kuaishou_industry_voucher_destroyed',
      {
        oid: voucher.oid,
        voucherCode: voucher.voucher_code,
        reason,
        source: input.source || 'admin_tool_manual_destroy',
        eticketType,
      },
      now,
    )
  }

  return {
    ok: true,
    voucher: updated,
    callbackSuccess: true,
    alreadyDestroyed,
    reason,
    task: task || null,
  }
}

export function normalizeKuaishouIndustryDestroyReason(value: unknown): string {
  const normalized = String(value || '')
    .trim()
    .toUpperCase()
  if (
    normalized === KUAISHOU_INDUSTRY_DESTROY_REASONS.ETICKET_EXPIRED ||
    normalized === KUAISHOU_INDUSTRY_DESTROY_REASONS.USER_APPLY_REFUND ||
    normalized === KUAISHOU_INDUSTRY_DESTROY_REASONS.SUPPLY_DESTROY ||
    normalized === KUAISHOU_INDUSTRY_DESTROY_REASONS.SYS_ADMIN_DESTROY
  ) {
    return normalized
  }

  return KUAISHOU_INDUSTRY_DESTROY_REASONS.SYS_ADMIN_DESTROY
}

function resolveTaskVouchers(task: TaskRow): Promise<KuaishouIndustryVoucherRow[]> {
  const taskId = Number(task?.id || 0)
  if (taskId > 0) {
    return listKuaishouIndustryVouchersByTaskId(taskId)
  }

  const context = parseJsonObject(task?.context_json)
  const voucher = parseJsonObject(context.kuaishouIndustryVoucher)
  const voucherCode = String(voucher.voucherCode || voucher.eticketId || '').trim()
  const oid = String(voucher.oid || task?.platform_order_id || '').trim()
  if (!voucherCode) {
    return Promise.resolve([])
  }

  return findKuaishouIndustryVoucherByCode(voucherCode, oid)
    .then((row) => (row ? [row] : []))
    .catch((error) => {
      logWarn('[kuaishou-industry/voucher]', '按上下文查询电子凭证失败', {
        taskId: task?.id || null,
        voucherCode,
        error: error instanceof Error ? error.message : String(error),
      })
      return []
    })
}

function resolveVoucherConsumeDetails(voucher: KuaishouIndustryVoucherRow): JsonObject[] {
  const parsed = parseJsonArray(voucher.consume_details_json)
  if (parsed.length > 0) {
    return parsed
  }

  if (normalizeVoucherStatus(voucher.status) !== 'CONSUMED' || !voucher.consume_serial_num) {
    return []
  }

  return [
    {
      serialNum: voucher.consume_serial_num,
      consumeType: KUAISHOU_INDUSTRY_DEFAULT_CONSUME_TYPE,
      consumeTime: voucher.consumed_at ? Date.parse(voucher.consumed_at) : 0,
    },
  ]
}

function appendConsumeDetail(details: JsonObject[], nextDetail: JsonObject): JsonObject[] {
  const serialNum = String(nextDetail.serialNum || '').trim()
  if (serialNum && details.some((item) => String(item.serialNum || '').trim() === serialNum)) {
    return details
  }

  return [...details, nextDetail]
}

function normalizeVoucherStatus(value: unknown): string {
  const normalized = String(value || '')
    .trim()
    .toUpperCase()
  if (normalized === 'CONSUMED' || normalized === 'DESTROYED') {
    return normalized
  }

  return 'UNUSED'
}

export function normalizeKuaishouIndustryConsumeType(value: unknown): string {
  return (
    String(value || KUAISHOU_INDUSTRY_DEFAULT_CONSUME_TYPE).trim() ||
    KUAISHOU_INDUSTRY_DEFAULT_CONSUME_TYPE
  )
}

function normalizePositiveTimestamp(value: unknown): number {
  const parsed = Number(value)
  return Number.isFinite(parsed) && parsed > 0 ? Math.trunc(parsed) : 0
}

function normalizePositiveInteger(value: unknown, fallback: number): number {
  const parsed = Number(value)
  return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback
}

function parseJsonObject(value: unknown): JsonObject {
  if (value && typeof value === 'object' && !Array.isArray(value)) {
    return value as JsonObject
  }

  try {
    const parsed = JSON.parse(String(value || '{}'))
    return asJsonObject(parsed)
  } catch {
    return {}
  }
}

function parseJsonArray(value: unknown): JsonObject[] {
  if (Array.isArray(value)) {
    return value.filter((item): item is JsonObject =>
      Boolean(item && typeof item === 'object' && !Array.isArray(item)),
    )
  }

  try {
    const parsed = JSON.parse(String(value || '[]'))
    return Array.isArray(parsed)
      ? parsed.filter((item): item is JsonObject =>
          Boolean(item && typeof item === 'object' && !Array.isArray(item)),
        )
      : []
  } catch {
    return []
  }
}
