import type { GetServerSideProps } from 'next'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { Card, Table, Address } from '@/libs/frontend-ui-primitives'
import Link from 'next/link'
import { transactionsApi, Transaction } from '@/services/api/transactions'
import { formatWeiAsEth } from '@/utils/format'
import PageIntro from '@/components/common/PageIntro'
import { fetchPublicJson } from '@/utils/publicExplorer'
import { normalizeTransaction } from '@/services/api/blockscout'
import { summarizeChainActivity } from '@/utils/activityContext'
import ExplorerFreshnessDisclosure from '@/components/common/ExplorerFreshnessDisclosure'
import PaginationControls from '@/components/common/PaginationControls'
import ListFilterBar from '@/components/common/ListFilterBar'
import SectionTabs, { type SectionTab } from '@/components/common/SectionTabs'
import { useListPageQuery } from '@/utils/useListPageQuery'
import type { ExplorerStats } from '@/services/api/stats'
import type { MissionControlBridgeStatusResponse } from '@/services/api/missionControl'
import { resolveEffectiveFreshness } from '@/utils/explorerFreshness'
import { fetchExplorerTruthContext } from '@/utils/serverExplorerContext'

interface TransactionsPageProps {
  initialTransactions: Transaction[]
  initialLatestBlocks: Array<{ number: number; timestamp: string }>
  initialStats: ExplorerStats | null
  initialBridgeStatus: MissionControlBridgeStatusResponse | null
}

function serializeTransactionList(transactions: Transaction[]): Transaction[] {
  return JSON.parse(
    JSON.stringify(
      transactions.map((transaction) => ({
        hash: transaction.hash,
        block_number: transaction.block_number,
        from_address: transaction.from_address,
        to_address: transaction.to_address ?? null,
        value: transaction.value,
        status: transaction.status ?? null,
        contract_address: transaction.contract_address ?? null,
        fee: transaction.fee ?? null,
        token_transfers: Array.isArray(transaction.token_transfers)
          ? transaction.token_transfers.map((transfer) => ({ token_address: transfer.token_address }))
          : [],
      })),
    ),
  ) as Transaction[]
}

type TransactionFilterTab = 'all' | 'success' | 'failed' | 'contracts' | 'transfers'

function matchesTransactionQuery(transaction: Transaction, query: string): boolean {
  const normalizedQuery = query.trim().toLowerCase()
  if (!normalizedQuery) return true
  const fields = [
    transaction.hash,
    transaction.from_address,
    transaction.to_address || '',
    transaction.contract_address || '',
  ]
  return fields.some((field) => field.toLowerCase().includes(normalizedQuery))
}

function matchesTransactionFilter(transaction: Transaction, filter: TransactionFilterTab): boolean {
  switch (filter) {
    case 'success':
      return transaction.status === 1
    case 'failed':
      return transaction.status != null && transaction.status !== 1
    case 'contracts':
      return Boolean(transaction.contract_address)
    case 'transfers':
      return (transaction.token_transfers?.length || 0) > 0
    default:
      return true
  }
}

export default function TransactionsPage({
  initialTransactions,
  initialLatestBlocks,
  initialStats,
  initialBridgeStatus,
}: TransactionsPageProps) {
  const pageSize = 15
  const [transactions, setTransactions] = useState<Transaction[]>(initialTransactions)
  const [latestBlocks, setLatestBlocks] = useState(initialLatestBlocks)
  const [stats, setStats] = useState<ExplorerStats | null>(initialStats)
  const [bridgeStatus, setBridgeStatus] = useState<MissionControlBridgeStatusResponse | null>(initialBridgeStatus)
  const [loading, setLoading] = useState(initialTransactions.length === 0)
  const [filterTab, setFilterTab] = useState<TransactionFilterTab>('all')
  const [filterQuery, setFilterQuery] = useState('')
  const { page, setPage } = useListPageQuery()
  const chainId = parseInt(process.env.NEXT_PUBLIC_CHAIN_ID || '138')
  const activityContext = useMemo(
    () =>
      summarizeChainActivity({
        blocks: latestBlocks.map((block) => ({
          chain_id: chainId,
          number: block.number,
          hash: '',
          timestamp: block.timestamp,
          miner: '',
          transaction_count: 0,
          gas_used: 0,
          gas_limit: 0,
        })),
        transactions,
        latestBlockNumber: latestBlocks[0]?.number ?? null,
        latestBlockTimestamp: latestBlocks[0]?.timestamp ?? null,
        freshness: resolveEffectiveFreshness(stats, bridgeStatus),
        diagnostics: stats?.diagnostics ?? bridgeStatus?.data?.diagnostics ?? null,
      }),
    [chainId, bridgeStatus, latestBlocks, stats, transactions],
  )

  const loadTransactions = useCallback(async () => {
    setLoading(true)
    try {
      const { ok, data } = await transactionsApi.listSafe(chainId, page, pageSize)
      setTransactions(ok ? data : [])
    } catch (error) {
      console.error('Failed to load transactions:', error)
      setTransactions([])
    } finally {
      setLoading(false)
    }
  }, [chainId, page, pageSize])

  useEffect(() => {
    if (page === 1 && initialTransactions.length > 0) {
      setTransactions(initialTransactions)
      setLoading(false)
      return
    }
    void loadTransactions()
  }, [initialTransactions, loadTransactions, page])

  useEffect(() => {
    if (initialLatestBlocks.length > 0 && initialStats && initialBridgeStatus) {
      setLatestBlocks(initialLatestBlocks)
      setStats(initialStats)
      setBridgeStatus(initialBridgeStatus)
      return
    }

    let active = true
    Promise.all([
      fetchPublicJson<{ items?: Array<{ height?: number | string | null; timestamp?: string | null }> }>(
        '/api/v2/blocks?page=1&page_size=3',
      ).catch(() => null),
      fetchExplorerTruthContext(),
    ])
      .then(([blocksResult, truthContext]) => {
        if (!active) return
        if (initialLatestBlocks.length === 0 && Array.isArray(blocksResult?.items)) {
          setLatestBlocks(
            blocksResult.items
              .map((item) => ({
                number: Number(item.height || 0),
                timestamp: item.timestamp || '',
              }))
              .filter((item) => Number.isFinite(item.number) && item.number > 0 && item.timestamp),
          )
        }
        setStats((current) => current ?? truthContext.initialStats)
        setBridgeStatus((current) => current ?? truthContext.initialBridgeStatus)
      })
      .catch(() => undefined)

    return () => {
      active = false
    }
  }, [initialBridgeStatus, initialLatestBlocks, initialStats])

  useEffect(() => {
    setPage(1)
  }, [filterTab, filterQuery, setPage])

  const filteredTransactions = useMemo(
    () => transactions.filter((transaction) => matchesTransactionFilter(transaction, filterTab) && matchesTransactionQuery(transaction, filterQuery)),
    [filterQuery, filterTab, transactions],
  )

  const paginatedTransactions = useMemo(() => {
    const start = (page - 1) * pageSize
    return filteredTransactions.slice(start, start + pageSize)
  }, [filteredTransactions, page, pageSize])

  const transactionSummary = useMemo(() => {
    const sampleSize = paginatedTransactions.length
    if (sampleSize === 0) {
      return {
        sampleSize: 0,
        successRate: 0,
        contractCreations: 0,
        tokenTransferTransactions: 0,
        averageFee: null as string | null,
      }
    }

    const successes = paginatedTransactions.filter((transaction) => transaction.status === 1).length
    const contractCreations = paginatedTransactions.filter((transaction) => Boolean(transaction.contract_address)).length
    const tokenTransferTransactions = paginatedTransactions.filter(
      (transaction) => (transaction.token_transfers?.length || 0) > 0,
    ).length
    const feeValues = paginatedTransactions
      .map((transaction) => {
        if (!transaction.fee) return null
        const numeric = Number(transaction.fee)
        return Number.isFinite(numeric) ? numeric : null
      })
      .filter((value): value is number => value != null)
    const averageFee =
      feeValues.length > 0
        ? formatWeiAsEth(Math.round(feeValues.reduce((sum, value) => sum + value, 0) / feeValues.length).toString(), 6)
        : null

    return {
      sampleSize,
      successRate: Math.round((successes / sampleSize) * 100),
      contractCreations,
      tokenTransferTransactions,
      averageFee,
    }
  }, [paginatedTransactions])

  const filterTabs = useMemo<SectionTab<TransactionFilterTab>[]>(() => {
    const countFor = (filter: TransactionFilterTab) =>
      transactions.filter((transaction) => matchesTransactionFilter(transaction, filter)).length

    return [
      { id: 'all', label: 'All', count: transactions.length },
      { id: 'success', label: 'Success', count: countFor('success') },
      { id: 'failed', label: 'Failed', count: countFor('failed') },
      { id: 'contracts', label: 'Contract creation', count: countFor('contracts') },
      { id: 'transfers', label: 'Token transfers', count: countFor('transfers') },
    ]
  }, [transactions])

  const showPagination = filteredTransactions.length > pageSize || page > 1
  const canGoNext = page * pageSize < filteredTransactions.length

  const columns = [
    {
      header: 'Hash',
      accessor: (tx: Transaction) => (
        <Link href={`/transactions/${tx.hash}`} className="text-primary-600 hover:underline">
          <Address address={tx.hash} truncate showCopy={false} />
        </Link>
      ),
    },
    {
      header: 'Block',
      accessor: (tx: Transaction) => (
        <Link href={`/blocks/${tx.block_number}`} className="text-primary-600 hover:underline">
          {tx.block_number}
        </Link>
      ),
    },
    {
      header: 'From',
      accessor: (tx: Transaction) => (
        <Link href={`/addresses/${tx.from_address}`} className="text-primary-600 hover:underline">
          <Address address={tx.from_address} truncate showCopy={false} />
        </Link>
      ),
    },
    {
      header: 'To',
      accessor: (tx: Transaction) => tx.to_address ? (
        <Link href={`/addresses/${tx.to_address}`} className="text-primary-600 hover:underline">
          <Address address={tx.to_address} truncate showCopy={false} />
        </Link>
      ) : <span className="text-gray-400">Contract Creation</span>,
    },
    {
      header: 'Value',
      accessor: (tx: Transaction) => formatWeiAsEth(tx.value),
    },
    {
      header: 'Status',
      accessor: (tx: Transaction) => (
        <span className={tx.status === 1 ? 'text-green-600' : 'text-red-600'}>
          {tx.status === 1 ? 'Success' : 'Failed'}
        </span>
      ),
    },
  ]

  return (
    <div className="container mx-auto px-4 py-4 sm:py-6">
      <PageIntro
        compact
        eyebrow="Indexed Flow"
        title="Transactions"
        description="Recent Chain 138 transactions with filters for status, contract creation, and token transfers."
        actions={[
          { href: '/blocks', label: 'Blocks' },
          { href: '/search', label: 'Search' },
        ]}
      />

      <ExplorerFreshnessDisclosure
        context={activityContext}
        stats={stats}
        bridgeStatus={bridgeStatus}
        activityTitle="Recency"
        scopeLabel="Indexed transaction activity on this page."
      />

      <SectionTabs
        tabs={filterTabs}
        activeTab={filterTab}
        onChange={setFilterTab}
        idPrefix="transactions-filter"
        ariaLabel="Transaction filters"
        className="-mx-4 mb-4 px-4"
      />

      <ListFilterBar
        query={filterQuery}
        onQueryChange={setFilterQuery}
        placeholder="Filter visible transactions by hash or address"
        resultCount={paginatedTransactions.length}
        totalCount={filteredTransactions.length}
        className="mb-4"
      />

      {!loading && paginatedTransactions.length > 0 && (
        <div className="mb-4 flex flex-wrap items-center gap-x-4 gap-y-1 rounded-lg border border-gray-200 bg-gray-50 px-3 py-2 text-sm text-gray-700 dark:border-gray-700 dark:bg-gray-900/40 dark:text-gray-300">
          <span>
            <span className="font-medium text-gray-900 dark:text-white">{transactionSummary.sampleSize}</span> on page
          </span>
          <span>
            <span className="font-medium text-gray-900 dark:text-white">{transactionSummary.successRate}%</span> success
          </span>
          <span>
            <span className="font-medium text-gray-900 dark:text-white">{transactionSummary.contractCreations}</span> contracts
          </span>
          <span>
            <span className="font-medium text-gray-900 dark:text-white">{transactionSummary.tokenTransferTransactions}</span> with transfers
          </span>
          {transactionSummary.averageFee ? (
            <span>
              avg fee <span className="font-medium text-gray-900 dark:text-white">{transactionSummary.averageFee}</span>
            </span>
          ) : null}
        </div>
      )}

      {loading ? (
        <Card>
          <p className="text-sm text-gray-600 dark:text-gray-400">Loading transactions...</p>
        </Card>
      ) : (
        <Table
          columns={columns}
          data={paginatedTransactions}
          emptyMessage={
            filterTab === 'all' && !filterQuery.trim()
              ? 'Recent transactions are unavailable right now.'
              : 'No transactions on this page match the current filter.'
          }
          keyExtractor={(tx) => tx.hash}
        />
      )}

      {showPagination ? (
        <PaginationControls
          page={page}
          onPageChange={setPage}
          label="Transactions"
          ariaLabel="Transactions pagination"
          disabled={loading}
          hasNextPage={canGoNext}
          className="mt-6"
        />
      ) : null}
    </div>
  )
}

export const getServerSideProps: GetServerSideProps<TransactionsPageProps> = async () => {
  const chainId = Number(process.env.NEXT_PUBLIC_CHAIN_ID || '138')
  const transactionsResult = await fetchPublicJson<{ items?: unknown[] }>('/api/v2/transactions?page=1&page_size=15').catch(
    () => null,
  )
  const initialTransactions = Array.isArray(transactionsResult?.items)
    ? transactionsResult.items.map((item) => normalizeTransaction(item as never, chainId))
    : []

  return {
    props: {
      initialTransactions: serializeTransactionList(initialTransactions),
      initialLatestBlocks: [],
      initialStats: null,
      initialBridgeStatus: null,
    },
  }
}
