import path from 'node:path'

import { APP_CONFIG_KEYS } from '../../config/app-config-keys.js'
import { PROJECT_ROOT } from '../../config/runtime.js'
import { readAppConfigEntry, saveAppConfigEntry } from '../config/app-config-store.js'
import { normalizeCloudtentaclesMatchName } from '../order/cloudtentacles-match-utils.js'
import type { JsonObject } from '../../types/json.js'
import { FULFILLMENT_EXECUTOR_KEYS } from './executors/types.js'

export type FulfillmentRoutingRuleMatchType = 'exact' | 'contains'

export type FulfillmentRoutingExecutorConfig = {
  enabled: boolean
}

export type FulfillmentRoutingRule = {
  id: string
  enabled: boolean
  productName: string
  normalizedProductName: string
  matchType: FulfillmentRoutingRuleMatchType
  executorKey: string
  priority: number
  notes: string
}

export type FulfillmentRoutingConfig = {
  enabled: boolean
  defaultExecutorPriority: string[]
  unmatchedExecutorKey: string
  executors: Record<string, FulfillmentRoutingExecutorConfig>
  rules: FulfillmentRoutingRule[]
}

export type FulfillmentRoutingCandidate = {
  executorKey: string
  available: boolean
  reason?: string
}

export type FulfillmentRouteResult = {
  selectedExecutorKey: string
  selectedRuleId: string
  reason: string
  matchedRule: null | {
    id: string
    productName: string
    executorKey: string
    priority: number
  }
  candidates: FulfillmentRoutingCandidate[]
  skipped: Array<{
    executorKey: string
    reason: string
  }>
}

const FULFILLMENT_ROUTING_CONFIG_FILE_PATH = path.join(
  PROJECT_ROOT,
  'data',
  'fulfillment-routing-config.json',
)

const ROUTABLE_EXECUTOR_KEYS: string[] = [
  FULFILLMENT_EXECUTOR_KEYS.KUAISHOU_CLOUD,
  FULFILLMENT_EXECUTOR_KEYS.KUAISHOU_FEIFEI,
  FULFILLMENT_EXECUTOR_KEYS.AFFILIATE_DASH,
  FULFILLMENT_EXECUTOR_KEYS.MANUAL_DISPATCH,
]

const DEFAULT_EXECUTOR_PRIORITY = [
  FULFILLMENT_EXECUTOR_KEYS.KUAISHOU_CLOUD,
  FULFILLMENT_EXECUTOR_KEYS.KUAISHOU_FEIFEI,
  FULFILLMENT_EXECUTOR_KEYS.AFFILIATE_DASH,
]

export function getFulfillmentRoutingConfigFilePath() {
  return FULFILLMENT_ROUTING_CONFIG_FILE_PATH
}

export function getFulfillmentRoutingConfig(): FulfillmentRoutingConfig {
  return readAppConfigEntry({
    configKey: APP_CONFIG_KEYS.fulfillmentRouting,
    legacyFilePath: FULFILLMENT_ROUTING_CONFIG_FILE_PATH,
    fallback: createDefaultFulfillmentRoutingConfig,
    normalize: normalizeFulfillmentRoutingConfig,
  })
}

export function saveFulfillmentRoutingConfig(rawValue: unknown): Promise<FulfillmentRoutingConfig> {
  return saveAppConfigEntry({
    configKey: APP_CONFIG_KEYS.fulfillmentRouting,
    legacyFilePath: FULFILLMENT_ROUTING_CONFIG_FILE_PATH,
    value: rawValue,
    normalize: normalizeFulfillmentRoutingConfig,
  })
}

export function normalizeFulfillmentRoutingConfig(rawValue: unknown): FulfillmentRoutingConfig {
  const source = isPlainObject(rawValue) ? rawValue : {}
  const sourceExecutors = isPlainObject(source.executors) ? source.executors : {}
  const rules = Array.isArray(source.rules) ? source.rules : []
  const defaultExecutorPriority = normalizeExecutorPriority(
    source.defaultExecutorPriority || source.executorPriority,
  )
  const unmatchedExecutorKey = normalizeExecutorKey(source.unmatchedExecutorKey)

  return {
    enabled: source.enabled !== false,
    defaultExecutorPriority:
      defaultExecutorPriority.length > 0 ? defaultExecutorPriority : [...DEFAULT_EXECUTOR_PRIORITY],
    unmatchedExecutorKey:
      unmatchedExecutorKey === FULFILLMENT_EXECUTOR_KEYS.MANUAL_DISPATCH
        ? unmatchedExecutorKey
        : '',
    executors: ROUTABLE_EXECUTOR_KEYS.reduce<Record<string, FulfillmentRoutingExecutorConfig>>(
      (result, executorKey) => {
        const executorConfig = isPlainObject(sourceExecutors[executorKey])
          ? (sourceExecutors[executorKey] as JsonObject)
          : {}
        result[executorKey] = {
          enabled: executorConfig.enabled !== false,
        }
        return result
      },
      {},
    ),
    rules: rules
      .map((rule) => normalizeFulfillmentRoutingRule(rule))
      .filter((rule): rule is FulfillmentRoutingRule => Boolean(rule))
      .sort((left, right) => right.priority - left.priority),
  }
}

export function resolveFulfillmentRoute({
  productName,
  candidates,
  config = getFulfillmentRoutingConfig(),
  preferredExecutorKey = '',
}: {
  productName: unknown
  candidates: FulfillmentRoutingCandidate[]
  config?: FulfillmentRoutingConfig
  preferredExecutorKey?: string
}): FulfillmentRouteResult {
  const normalizedConfig = normalizeFulfillmentRoutingConfig(config)
  const normalizedCandidates = normalizeRoutingCandidates(candidates)
  const skipped: FulfillmentRouteResult['skipped'] = []

  if (normalizedConfig.enabled === false) {
    // 总开关关闭 = 停止自动履约：不再回退默认配置，订单进入待补全队列（isConfigured=false）
    return createEmptyRouteResult(normalizedCandidates, skipped, {
      reason: '履约路由未启用，自动发货已停止',
    })
  }

  // 指定匹配（显式映射）最高优先：命中且通道可用时直接选择，不受全局优先级影响。
  const preferred = normalizeExecutorKey(preferredExecutorKey)
  if (preferred) {
    const selected = resolveExecutorSelection({
      executorKey: preferred,
      candidates: normalizedCandidates,
      config: normalizedConfig,
      skipped,
      reason: '命中指定匹配（显式映射），优先选择',
      matchedRule: null,
    })
    if (selected.selectedExecutorKey) {
      return selected
    }
  }

  const matchedRule = findMatchedRoutingRule(productName, normalizedConfig.rules)
  if (matchedRule) {
    const selected = resolveExecutorSelection({
      executorKey: matchedRule.executorKey,
      candidates: normalizedCandidates,
      config: normalizedConfig,
      skipped,
      reason: '命中商品路由规则',
      matchedRule,
    })
    if (selected.selectedExecutorKey) {
      return selected
    }

    const fallback = resolveByExecutorPriority({
      priority: normalizedConfig.defaultExecutorPriority.filter(
        (executorKey) => executorKey !== matchedRule.executorKey,
      ),
      candidates: normalizedCandidates,
      config: normalizedConfig,
      skipped,
      reasonPrefix: '商品路由规则目标不可用，按全局优先级回退',
    })
    if (fallback.selectedExecutorKey) {
      return fallback
    }

    return {
      ...selected,
      reason: selected.reason || '商品路由规则目标通道不可用，且未命中其他履约通道',
    }
  }

  const selected = resolveByExecutorPriority({
    priority: normalizedConfig.defaultExecutorPriority,
    candidates: normalizedCandidates,
    config: normalizedConfig,
    skipped,
    reasonPrefix: '按全局优先级命中',
  })
  if (selected.selectedExecutorKey) {
    return selected
  }

  if (normalizedConfig.unmatchedExecutorKey === FULFILLMENT_EXECUTOR_KEYS.MANUAL_DISPATCH) {
    const fallback = resolveExecutorSelection({
      executorKey: FULFILLMENT_EXECUTOR_KEYS.MANUAL_DISPATCH,
      candidates: normalizedCandidates,
      config: normalizedConfig,
      skipped,
      reason: '未命中自动履约通道，进入人工履约',
      matchedRule: null,
    })
    if (fallback.selectedExecutorKey) {
      return fallback
    }
  }

  return {
    selectedExecutorKey: '',
    selectedRuleId: '',
    reason: '未命中可用履约通道',
    matchedRule: null,
    candidates: normalizedCandidates,
    skipped,
  }
}

function resolveByExecutorPriority({
  priority,
  candidates,
  config,
  skipped,
  reasonPrefix,
}: {
  priority: string[]
  candidates: FulfillmentRoutingCandidate[]
  config: FulfillmentRoutingConfig
  skipped: FulfillmentRouteResult['skipped']
  reasonPrefix: string
}) {
  for (const executorKey of priority) {
    const selected = resolveExecutorSelection({
      executorKey,
      candidates,
      config,
      skipped,
      reason: reasonPrefix,
      matchedRule: null,
    })
    if (selected.selectedExecutorKey) {
      return selected
    }
  }

  return {
    selectedExecutorKey: '',
    selectedRuleId: '',
    reason: '',
    matchedRule: null,
    candidates,
    skipped,
  }
}

function resolveExecutorSelection({
  executorKey,
  candidates,
  config,
  skipped,
  reason,
  matchedRule,
}: {
  executorKey: string
  candidates: FulfillmentRoutingCandidate[]
  config: FulfillmentRoutingConfig
  skipped: FulfillmentRouteResult['skipped']
  reason: string
  matchedRule: FulfillmentRoutingRule | null
}): FulfillmentRouteResult {
  const normalizedExecutorKey = normalizeExecutorKey(executorKey)
  if (!normalizedExecutorKey) {
    return createEmptyRouteResult(candidates, skipped)
  }

  if (config.executors[normalizedExecutorKey]?.enabled === false) {
    skipped.push({
      executorKey: normalizedExecutorKey,
      reason: '履约通道已停用',
    })
    return createEmptyRouteResult(candidates, skipped)
  }

  if (normalizedExecutorKey === FULFILLMENT_EXECUTOR_KEYS.MANUAL_DISPATCH) {
    return createSelectedRouteResult({
      executorKey: normalizedExecutorKey,
      reason,
      matchedRule,
      candidates,
      skipped,
    })
  }

  const candidate = candidates.find((item) => item.executorKey === normalizedExecutorKey)
  if (!candidate?.available) {
    skipped.push({
      executorKey: normalizedExecutorKey,
      reason: candidate?.reason || '未命中该通道商品配置',
    })
    return createEmptyRouteResult(candidates, skipped)
  }

  return createSelectedRouteResult({
    executorKey: normalizedExecutorKey,
    reason,
    matchedRule,
    candidates,
    skipped,
  })
}

function createSelectedRouteResult({
  executorKey,
  reason,
  matchedRule,
  candidates,
  skipped,
}: {
  executorKey: string
  reason: string
  matchedRule: FulfillmentRoutingRule | null
  candidates: FulfillmentRoutingCandidate[]
  skipped: FulfillmentRouteResult['skipped']
}): FulfillmentRouteResult {
  return {
    selectedExecutorKey: executorKey,
    selectedRuleId: matchedRule?.id || '',
    reason,
    matchedRule: matchedRule
      ? {
          id: matchedRule.id,
          productName: matchedRule.productName,
          executorKey: matchedRule.executorKey,
          priority: matchedRule.priority,
        }
      : null,
    candidates,
    skipped,
  }
}

function createEmptyRouteResult(
  candidates: FulfillmentRoutingCandidate[],
  skipped: FulfillmentRouteResult['skipped'],
  options: { reason?: string } = {},
): FulfillmentRouteResult {
  return {
    selectedExecutorKey: '',
    selectedRuleId: '',
    reason: options.reason || '',
    matchedRule: null,
    candidates,
    skipped,
  }
}

function findMatchedRoutingRule(productName: unknown, rules: FulfillmentRoutingRule[]) {
  const normalizedProductName = normalizeCloudtentaclesMatchName(productName)
  if (!normalizedProductName) {
    return null
  }

  return (
    rules.find((rule) => {
      if (rule.enabled === false || !rule.normalizedProductName) {
        return false
      }

      if (rule.matchType === 'contains') {
        return normalizedProductName.includes(rule.normalizedProductName)
      }

      return normalizedProductName === rule.normalizedProductName
    }) || null
  )
}

function normalizeFulfillmentRoutingRule(rawValue: unknown): FulfillmentRoutingRule | null {
  const source = isPlainObject(rawValue) ? rawValue : {}
  const productName = String(source.productName || source.name || '').trim()
  const normalizedProductName =
    normalizeCloudtentaclesMatchName(source.normalizedProductName) ||
    normalizeCloudtentaclesMatchName(productName)
  const executorKey = normalizeExecutorKey(source.executorKey)

  if (!productName || !normalizedProductName || !executorKey) {
    return null
  }

  return {
    id:
      String(source.id || `${normalizedProductName}:${executorKey}`).trim() ||
      `${normalizedProductName}:${executorKey}`,
    enabled: source.enabled !== false,
    productName,
    normalizedProductName,
    matchType: source.matchType === 'contains' ? 'contains' : 'exact',
    executorKey,
    priority: normalizeInteger(source.priority, 100),
    notes: String(source.notes || '').trim(),
  }
}

function normalizeRoutingCandidates(candidates: FulfillmentRoutingCandidate[]) {
  const result = new Map<string, FulfillmentRoutingCandidate>()

  for (const candidate of Array.isArray(candidates) ? candidates : []) {
    const executorKey = normalizeExecutorKey(candidate.executorKey)
    if (!executorKey || executorKey === FULFILLMENT_EXECUTOR_KEYS.MANUAL_DISPATCH) {
      continue
    }
    result.set(executorKey, {
      executorKey,
      available: candidate.available === true,
      reason: String(candidate.reason || '').trim(),
    })
  }

  return Array.from(result.values())
}

function normalizeExecutorPriority(value: unknown) {
  const rawItems = Array.isArray(value) ? value : []
  const seen = new Set<string>()
  const items: string[] = []

  for (const item of rawItems) {
    const executorKey = normalizeExecutorKey(item)
    if (
      !executorKey ||
      seen.has(executorKey) ||
      executorKey === FULFILLMENT_EXECUTOR_KEYS.MANUAL_DISPATCH
    ) {
      continue
    }
    seen.add(executorKey)
    items.push(executorKey)
  }

  // 历史配置不会自动拥有后来新增的履约通道；补全默认通道，开关仍由 executors 控制。
  for (const executorKey of DEFAULT_EXECUTOR_PRIORITY) {
    if (!seen.has(executorKey)) {
      seen.add(executorKey)
      items.push(executorKey)
    }
  }

  return items
}

function normalizeExecutorKey(value: unknown) {
  const executorKey = String(value || '').trim()
  return ROUTABLE_EXECUTOR_KEYS.includes(executorKey) ? executorKey : ''
}

function normalizeInteger(value: unknown, fallback: number) {
  const parsed = Number(value)
  return Number.isInteger(parsed) ? parsed : fallback
}

function createDefaultFulfillmentRoutingConfig(): FulfillmentRoutingConfig {
  return {
    enabled: true,
    defaultExecutorPriority: [...DEFAULT_EXECUTOR_PRIORITY],
    unmatchedExecutorKey: '',
    executors: ROUTABLE_EXECUTOR_KEYS.reduce<Record<string, FulfillmentRoutingExecutorConfig>>(
      (result, executorKey) => {
        result[executorKey] = { enabled: true }
        return result
      },
      {},
    ),
    rules: [],
  }
}

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