import { apiRequest } from './api.js'

function toNumber(value, fallback = 0) {
  const number = Number(value)
  return Number.isFinite(number) ? number : fallback
}

function toArray(value) {
  return Array.isArray(value) ? value : []
}

function toObject(value) {
  return value && typeof value === 'object' && !Array.isArray(value) ? value : {}
}

function uniqueTexts(value, limit = Number.POSITIVE_INFINITY) {
  return [...new Set(
    toArray(value)
      .map((item) => String(item || '').trim())
      .filter(Boolean)
  )].slice(0, limit)
}

export function createRiskDispositionRequestId(action, observationId) {
  const normalizedAction = String(action || 'action').trim() || 'action'
  const normalizedObservationId = String(observationId || 'risk').trim() || 'risk'
  if (typeof globalThis.crypto?.randomUUID === 'function') {
    return `risk:${normalizedAction}:${normalizedObservationId}:${globalThis.crypto.randomUUID()}`
  }
  return `risk:${normalizedAction}:${normalizedObservationId}:${Date.now()}:${Math.random().toString(16).slice(2)}`
}

export function normalizeRiskDisposition(item = {}) {
  if (!item || typeof item !== 'object' || !String(item.id || '').trim()) {
    return null
  }
  return {
    id: String(item.id || '').trim(),
    observationId: String(item.observation_id || item.observationId || '').trim(),
    adjudication: String(item.adjudication || 'unreviewed').trim(),
    lifecycleStatus: String(item.lifecycle_status || item.lifecycleStatus || 'open').trim(),
    version: Math.max(0, toNumber(item.version)),
    assignee: String(item.assignee || '').trim(),
    dueAt: String(item.due_at || item.dueAt || '').trim(),
    resolution: String(item.resolution || '').trim(),
    availableActions: uniqueTexts(item.available_actions ?? item.availableActions),
    readOnlyReason: String(item.read_only_reason ?? item.readOnlyReason ?? '').trim(),
    waiverRequesterId: String(
      item.waiver_requester_id || item.waiverRequesterId || ''
    ).trim(),
    waiverRequesterName: String(
      item.waiver_requester_name || item.waiverRequesterName || ''
    ).trim(),
    waiverRequestedAt: String(
      item.waiver_requested_at || item.waiverRequestedAt || ''
    ).trim(),
    waiverReason: String(item.waiver_reason || item.waiverReason || '').trim(),
    waiverScope: String(item.waiver_scope || item.waiverScope || '').trim(),
    waiverExpiresAt: String(
      item.waiver_expires_at || item.waiverExpiresAt || ''
    ).trim(),
    waiverConditions: uniqueTexts(
      item.waiver_conditions_json
        ?? item.waiver_conditions
        ?? item.waiverConditions
    ),
    waiverDecision: String(item.waiver_decision || item.waiverDecision || '').trim(),
    waiverDeciderId: String(item.waiver_decider_id || item.waiverDeciderId || '').trim(),
    waiverDeciderName: String(
      item.waiver_decider_name || item.waiverDeciderName || ''
    ).trim(),
    waiverDecidedAt: String(item.waiver_decided_at || item.waiverDecidedAt || '').trim(),
    waiverDecisionReason: String(
      item.waiver_decision_reason || item.waiverDecisionReason || ''
    ).trim(),
    createdAt: String(item.created_at || item.createdAt || '').trim(),
    updatedAt: String(item.updated_at || item.updatedAt || '').trim(),
    events: toArray(item.events).map((event) => ({
      id: String(event?.id || '').trim(),
      action: String(event?.action || '').trim(),
      version: Math.max(0, toNumber(event?.version)),
      actorId: String(event?.actor_id || event?.actorId || '').trim(),
      actorName: String(event?.actor_name || event?.actorName || '').trim(),
      requestId: String(event?.request_id || event?.requestId || '').trim(),
      comment: String(event?.comment || '').trim(),
      beforeState: toObject(event?.before_json || event?.beforeState),
      afterState: toObject(event?.after_json || event?.afterState),
      createdAt: String(event?.created_at || event?.createdAt || '').trim()
    }))
  }
}

export function normalizeRiskObservation(item = {}) {
  return {
    id: String(item.id || '').trim(),
    observationKey: String(item.observation_key || item.observationKey || '').trim(),
    claimId: String(item.claim_id || item.claimId || '').trim(),
    claimNo: String(item.claim_no || item.claimNo || '').trim(),
    riskType: String(item.risk_type || item.riskType || '').trim(),
    riskSignal: String(item.risk_signal || item.riskSignal || '').trim(),
    title: String(item.title || '').trim(),
    description: String(item.description || '').trim(),
    riskScore: toNumber(item.risk_score ?? item.riskScore),
    riskLevel: String(item.risk_level || item.riskLevel || '').trim(),
    confidenceScore: toNumber(item.confidence_score ?? item.confidenceScore),
    controlStage: String(item.control_stage || item.controlStage || '').trim(),
    controlMode: String(item.control_mode || item.controlMode || '').trim(),
    automationMode: String(item.automation_mode || item.automationMode || '').trim(),
    source: String(item.source || '').trim(),
    algorithmVersion: String(item.algorithm_version || item.algorithmVersion || '').trim(),
    status: String(item.status || '').trim(),
    feedbackStatus: String(item.feedback_status || item.feedbackStatus || '').trim(),
    contributionScores: toObject(
      item.contribution_scores_json || item.contribution_scores || item.contributionScores
    ),
    baseline: toObject(item.baseline_json || item.baseline),
    evidence: toArray(item.evidence_json || item.evidence),
    graphNodeKeys: toArray(
      item.graph_node_keys_json || item.graph_node_keys || item.graphNodeKeys
    ),
    graphEdgeKeys: toArray(
      item.graph_edge_keys_json || item.graph_edge_keys || item.graphEdgeKeys
    ),
    policyRefs: toArray(item.policy_refs_json || item.policy_refs || item.policyRefs),
    similarCaseClaimIds: toArray(
      item.similar_case_claim_ids_json
        || item.similar_case_claim_ids
        || item.similarCaseClaimIds
    ),
    ontology: toObject(item.ontology_json || item.ontologyJson),
    decisionTrace: toObject(
      item.decision_trace_json || item.decision_trace || item.decisionTrace
    ),
    feedbackItems: toArray(item.feedback_items || item.feedbackItems),
    disposition: normalizeRiskDisposition(item.disposition),
    availableActions: uniqueTexts(item.available_actions ?? item.availableActions),
    readOnlyReason: String(item.read_only_reason ?? item.readOnlyReason ?? '').trim(),
    createdAt: String(item.created_at || item.createdAt || '').trim(),
    updatedAt: String(item.updated_at || item.updatedAt || '').trim()
  }
}

export function normalizeRiskObservationDashboard(payload = {}) {
  return {
    windowDays: toNumber(payload.window_days ?? payload.windowDays, 30),
    totalObservations: toNumber(payload.total_observations ?? payload.totalObservations),
    pendingCount: toNumber(payload.pending_count ?? payload.pendingCount),
    riskClueCount: toNumber(payload.risk_clue_count ?? payload.riskClueCount),
    highOrAboveCount: toNumber(payload.high_or_above_count ?? payload.highOrAboveCount),
    confirmedCount: toNumber(payload.confirmed_count ?? payload.confirmedCount),
    falsePositiveCount: toNumber(payload.false_positive_count ?? payload.falsePositiveCount),
    feedbackSampleCount: toNumber(
      payload.feedback_sample_count ?? payload.feedbackSampleCount
    ),
    totalAmount: toNumber(payload.total_amount ?? payload.totalAmount),
    averageScore: toNumber(payload.average_score ?? payload.averageScore),
    confirmationRate: toNumber(payload.confirmation_rate ?? payload.confirmationRate),
    falsePositiveRate: toNumber(payload.false_positive_rate ?? payload.falsePositiveRate),
    candidateRuleCount: toNumber(payload.candidate_rule_count ?? payload.candidateRuleCount),
    levelDistribution: toObject(payload.level_distribution || payload.levelDistribution),
    statusDistribution: toObject(payload.status_distribution || payload.statusDistribution),
    signalDistribution: toObject(payload.signal_distribution || payload.signalDistribution),
    sourceDistribution: toObject(payload.source_distribution || payload.sourceDistribution),
    automationDistribution: toObject(
      payload.automation_distribution || payload.automationDistribution
    ),
    departmentDistribution: toObject(
      payload.department_distribution || payload.departmentDistribution
    ),
    expenseTypeDistribution: toObject(
      payload.expense_type_distribution || payload.expenseTypeDistribution
    ),
    riskTypeDistribution: toObject(payload.risk_type_distribution || payload.riskTypeDistribution),
    supplierDistribution: toObject(payload.supplier_distribution || payload.supplierDistribution),
    employeeGradeDistribution: toObject(
      payload.employee_grade_distribution || payload.employeeGradeDistribution
    ),
    dailyTrend: toArray(payload.daily_trend || payload.dailyTrend),
    topRiskSignals: toArray(payload.top_risk_signals || payload.topRiskSignals),
    topDepartments: toArray(payload.top_departments || payload.topDepartments),
    topEmployees: toArray(payload.top_employees || payload.topEmployees),
    topSuppliers: toArray(payload.top_suppliers || payload.topSuppliers),
    topExpenseTypes: toArray(payload.top_expense_types || payload.topExpenseTypes),
    topRules: toArray(payload.top_rules || payload.topRules),
    recentHighObservations: toArray(
      payload.recent_high_observations || payload.recentHighObservations
    ).map(normalizeRiskObservation)
  }
}

export async function fetchRiskObservationDashboard(options = {}) {
  const windowDays = Math.max(1, Math.min(toNumber(options.windowDays || 30), 365))
  const limit = Math.max(1, Math.min(toNumber(options.limit || 500), 2000))
  const search = new URLSearchParams()
  search.set('window_days', String(windowDays))
  search.set('limit', String(limit))

  const payload = await apiRequest(`/risk-observations/dashboard?${search.toString()}`, {
    timeoutMs: toNumber(options.timeoutMs || 3500),
    timeoutMessage: '风险看板数据加载超时，请稍后重试。'
  })

  return normalizeRiskObservationDashboard(payload)
}

export async function fetchClaimRiskObservations(claimId, options = {}) {
  const normalizedClaimId = String(claimId || '').trim()
  if (!normalizedClaimId) {
    return []
  }

  const payload = await apiRequest(
    `/risk-observations/claim/${encodeURIComponent(normalizedClaimId)}`,
    {
      timeoutMs: toNumber(options.timeoutMs || 3500),
      timeoutMessage: '风险证据链加载超时，请稍后重试。'
    }
  )

  return toArray(payload).map(normalizeRiskObservation)
}

export async function fetchRunRiskObservations(runId, options = {}) {
  const normalizedRunId = String(runId || '').trim()
  if (!normalizedRunId) {
    return []
  }

  const search = new URLSearchParams()
  search.set('run_id', normalizedRunId)
  search.set('limit', String(Math.max(1, Math.min(toNumber(options.limit || 100), 200))))

  const payload = await apiRequest(`/risk-observations?${search.toString()}`, {
    timeoutMs: toNumber(options.timeoutMs || 3500),
    timeoutMessage: '本次运行风险观察加载超时，请稍后重试。'
  })

  return toArray(payload.items || payload).map(normalizeRiskObservation)
}

export async function executeRiskDispositionAction(observationId, payload = {}) {
  const normalizedObservationId = String(observationId || '').trim()
  const action = String(payload.action || '').trim()
  if (!normalizedObservationId || !action) {
    throw new Error('风险处置缺少观察标识或动作。')
  }
  const requestId = String(payload.requestId || payload.request_id || '').trim()
    || createRiskDispositionRequestId(action, normalizedObservationId)
  const body = {
    action,
    expected_version: Math.max(0, toNumber(payload.expectedVersion ?? payload.expected_version)),
    request_id: requestId
  }
  const comment = String(payload.comment || '').trim()
  const resolution = String(payload.resolution || '').trim()
  const assignee = String(payload.assignee || '').trim()
  const dueAt = String(payload.dueAt || payload.due_at || '').trim()
  const waiverReason = String(payload.waiverReason || payload.waiver_reason || '').trim()
  const waiverScope = String(payload.waiverScope || payload.waiver_scope || '').trim()
  const waiverExpiresAt = String(
    payload.waiverExpiresAt || payload.waiver_expires_at || ''
  ).trim()
  const waiverConditions = uniqueTexts(
    payload.waiverConditions ?? payload.waiver_conditions,
    20
  )
  if (comment) body.comment = comment
  if (resolution) body.resolution = resolution
  if (assignee) body.assignee = assignee
  if (dueAt) body.due_at = dueAt
  if (waiverReason) body.waiver_reason = waiverReason
  if (waiverScope) body.waiver_scope = waiverScope
  if (waiverExpiresAt) body.waiver_expires_at = waiverExpiresAt
  if (waiverConditions.length) body.waiver_conditions = waiverConditions

  const result = await apiRequest(
    `/risk-observations/${encodeURIComponent(normalizedObservationId)}/disposition/actions`,
    {
      method: 'POST',
      body: JSON.stringify(body),
      timeoutMs: toNumber(payload.timeoutMs || 5000),
      timeoutMessage: '风险处置提交超时，请刷新状态后重试。'
    }
  )
  return {
    disposition: normalizeRiskDisposition(result?.disposition),
    event: result?.event && typeof result.event === 'object' ? result.event : {},
    replayed: Boolean(result?.replayed),
    requestId
  }
}

export function normalizeRiskDispositionError(error) {
  const code = String(error?.code || '').trim()
  const status = Number(error?.status || 0)
  const serverMessage = String(error?.message || '').trim()
  if (code === 'RISK_DISPOSITION_VERSION_CONFLICT') {
    return {
      code,
      title: '风险状态已更新',
      message: serverMessage || '其他处理人已更新该风险，请刷新后重新确认。',
      shouldRefresh: true
    }
  }
  if (code === 'RISK_WAIVER_DECISION_FORBIDDEN' || status === 403) {
    return {
      code: code || 'RISK_DISPOSITION_FORBIDDEN',
      title: '当前账号不能执行此操作',
      message: serverMessage || '服务端未授予当前风险处置动作。',
      shouldRefresh: true
    }
  }
  if (code === 'REQUEST_TIMEOUT') {
    return {
      code,
      title: '提交结果暂未确认',
      message: serverMessage || '请求已超时，请刷新状态后再决定是否重试。',
      shouldRefresh: true
    }
  }
  if (status === 409) {
    return {
      code: code || 'RISK_DISPOSITION_CONFLICT',
      title: '当前状态不允许该操作',
      message: serverMessage || '风险处置状态发生冲突，请刷新后重试。',
      shouldRefresh: true
    }
  }
  if (status === 422) {
    return {
      code: code || 'RISK_DISPOSITION_VALIDATION_FAILED',
      title: '提交内容不完整',
      message: serverMessage || '请检查必填项、有效期和输入长度。',
      shouldRefresh: false
    }
  }
  return {
    code: code || 'RISK_DISPOSITION_REQUEST_FAILED',
    title: '风险处置提交失败',
    message: serverMessage || '请稍后重试；如持续失败，请联系系统管理员。',
    shouldRefresh: false
  }
}
