import type { Block } from '@/services/api/blocks'
import type { Transaction } from '@/services/api/transactions'
import type { ExplorerFreshnessDiagnostics, ExplorerFreshnessSnapshot } from '@/services/api/stats'

export type ChainActivityState = 'active' | 'low' | 'inactive' | 'unknown'

export interface ChainActivityContext {
  latest_block_number: number | null
  latest_block_timestamp: string | null
  latest_transaction_block_number: number | null
  latest_transaction_timestamp: string | null
  last_non_empty_block_number: number | null
  last_non_empty_block_timestamp: string | null
  block_gap_to_latest_transaction: number | null
  latest_transaction_age_seconds: number | null
  state: ChainActivityState
  head_is_idle: boolean
  transaction_visibility_unavailable: boolean
}

function sortDescending(values: number[]): number[] {
  return [...values].sort((left, right) => right - left)
}

function toTimestamp(value?: string | null): number | null {
  if (!value) return null
  const parsed = Date.parse(value)
  return Number.isFinite(parsed) ? parsed : null
}

export function summarizeChainActivity(input: {
  blocks?: Block[]
  transactions?: Transaction[]
  latestBlockNumber?: number | null
  latestBlockTimestamp?: string | null
  freshness?: ExplorerFreshnessSnapshot | null
  diagnostics?: ExplorerFreshnessDiagnostics | null
}): ChainActivityContext {
  const freshness = input.freshness || null
  const diagnostics = input.diagnostics || null
  const blocks = Array.isArray(input.blocks) ? input.blocks : []
  const transactions = Array.isArray(input.transactions) ? input.transactions : []

  const latestBlockFromList = sortDescending(blocks.map((block) => block.number).filter((value) => Number.isFinite(value)))[0] ?? null
  const latestBlock = freshness?.chain_head.block_number ?? input.latestBlockNumber ?? latestBlockFromList
  const latestBlockTimestamp =
    freshness?.chain_head.timestamp ??
    input.latestBlockTimestamp ??
    blocks.find((block) => block.number === latestBlock)?.timestamp ??
    blocks[0]?.timestamp ??
    null

  const latestTransaction = freshness?.latest_indexed_transaction.block_number ?? sortDescending(
    transactions.map((transaction) => transaction.block_number).filter((value) => Number.isFinite(value)),
  )[0] ?? null
  const latestTransactionRecord =
    transactions.find((transaction) => transaction.block_number === latestTransaction) ?? transactions[0] ?? null

  const nonEmptyBlock =
    diagnostics?.latest_non_empty_block_from_block_feed?.block_number ??
    freshness?.latest_non_empty_block.block_number ??
    sortDescending(blocks.filter((block) => block.transaction_count > 0).map((block) => block.number))[0] ?? latestTransaction
  const nonEmptyBlockTimestamp =
    diagnostics?.latest_non_empty_block_from_block_feed?.timestamp ??
    freshness?.latest_non_empty_block.timestamp ??
    blocks.find((block) => block.number === nonEmptyBlock)?.timestamp ??
    latestTransactionRecord?.created_at ??
    null

  const latestTransactionTimestamp = freshness?.latest_indexed_transaction.timestamp ?? latestTransactionRecord?.created_at ?? null
  const transactionVisibilityUnavailable =
    freshness?.latest_indexed_transaction.source === 'unavailable' ||
    freshness?.latest_indexed_transaction.completeness === 'unavailable'
  const latestTransactionAgeSeconds =
    freshness?.latest_indexed_transaction.age_seconds ??
    (() => {
      const timestamp = toTimestamp(latestTransactionTimestamp)
      if (timestamp == null) return null
      return Math.max(0, Math.round((Date.now() - timestamp) / 1000))
    })()

  const gap = freshness?.latest_non_empty_block.distance_from_head ??
    diagnostics?.tx_lag_blocks ??
    (latestBlock != null && latestTransaction != null
      ? Math.max(0, latestBlock - latestTransaction)
      : null)

  const state: ChainActivityState = (() => {
    switch (diagnostics?.activity_state) {
      case 'active':
        return 'active'
      case 'sparse_activity':
      case 'quiet_chain':
        return 'low'
      case 'fresh_head_stale_transaction_visibility':
        return 'inactive'
      case 'limited_observability':
        return 'unknown'
      default:
        return latestTransactionAgeSeconds == null
          ? 'unknown'
          : latestTransactionAgeSeconds <= 15 * 60
            ? 'active'
            : latestTransactionAgeSeconds <= 3 * 60 * 60
              ? 'low'
              : 'inactive'
    }
  })()

  const headIsIdle =
    diagnostics?.activity_state === 'quiet_chain' ||
    (gap != null &&
      gap > 0 &&
      latestTransactionAgeSeconds != null &&
      latestTransactionAgeSeconds > 0)

  return {
    latest_block_number: latestBlock,
    latest_block_timestamp: latestBlockTimestamp,
    latest_transaction_block_number: latestTransaction,
    latest_transaction_timestamp: latestTransactionTimestamp,
    last_non_empty_block_number: nonEmptyBlock,
    last_non_empty_block_timestamp: nonEmptyBlockTimestamp,
    block_gap_to_latest_transaction: gap,
    latest_transaction_age_seconds: latestTransactionAgeSeconds,
    state,
    head_is_idle: headIsIdle,
    transaction_visibility_unavailable: transactionVisibilityUnavailable,
  }
}
