import type { GetServerSideProps } from 'next'
import { useEffect, useMemo, useState } from 'react'
import { useRouter } from 'next/router'
import { Card, Address } from '@/libs/frontend-ui-primitives'
import Link from 'next/link'
import type { TokenListToken } from '@/services/api/config'
import { tokensApi } from '@/services/api/tokens'
import { tokenAggregationApi, type TokenAggregationTokenSnapshot } from '@/services/api/tokenAggregation'
import EntityBadge from '@/components/common/EntityBadge'
import PostureBadge from '@/components/common/PostureBadge'
import {
  inferDirectSearchTarget,
  inferEnsSearchTarget,
  inferTokenSearchTarget,
  normalizeExplorerSearchResults,
  searchWrappedTransportTokens,
  shouldDeferChain138AddressJump,
  suggestCuratedTokens,
  type RawExplorerSearchItem,
  type WrappedTransportRegistryRow,
} from '@/utils/search'
import PageIntro from '@/components/common/PageIntro'
import CompactMetricBar from '@/components/common/CompactMetricBar'
import DisclosureSection from '@/components/common/DisclosureSection'
import PaginationControls from '@/components/common/PaginationControls'
import SectionTabs, { TabPanel, type SectionTab } from '@/components/common/SectionTabs'
import { fetchPublicJson } from '@/utils/publicExplorer'
import { fetchTokenListForSurface } from '@/services/api/tokenListSurfaces'
import { useUiMode } from '@/components/common/UiModeContext'
import MarketEvidenceNote from '@/components/common/MarketEvidenceNote'
import TokenLiquidityStack from '@/components/wallet/TokenLiquidityStack'
import {
  externalChainExplorerUrl,
  fetchCwRegistry,
  flattenCwRegistry,
  type WrappedTransportTokenRow,
} from '@/services/api/cwRegistry'
import {
  fetchTokenMappingPairs,
  getMeshDestinationChainIds,
  resolveMeshFromHub,
  resolveTokenMapping,
  type TokenMappingPair,
} from '@/services/api/tokenMapping'
import {
  buildChainNameMap,
  buildMeshCounterpartRows,
  inferMeshSeed,
  type MeshCounterpartRow,
} from '@/utils/meshCounterparts'
import { fetchPolygonMapper, type PolygonMapperResponse } from '@/services/api/polygonMapper'
import { resolveEnsAddress, isEnsName } from '@/utils/ens'
import { resolveAddressFromRegistryEns } from '@/utils/web3IdentityRegistry'
import { searchCuratedPools, type PoolSearchRow } from '@/utils/poolSearch'
import { fetchPoolRegistry, type CuratedPoolRegistryEntry } from '@/services/api/liquidityPositions'

type SearchFilterMode = 'all' | 'gru' | 'x402' | 'wrapped'
type SearchResultsTab = 'indexed' | 'mesh' | 'pools' | 'wrapped'

const SEARCH_INDEXED_PAGE_SIZE = 4

interface SearchPageProps {
  initialQuery: string
  initialRawResults: RawExplorerSearchItem[]
  initialCuratedTokens: TokenListToken[]
}

function formatUsd(value: number | undefined): string {
  if (value == null || !Number.isFinite(value)) return 'Unavailable'
  return new Intl.NumberFormat('en-US', {
    style: 'currency',
    currency: 'USD',
    maximumFractionDigits: value >= 100 ? 0 : 2,
  }).format(value)
}

export default function SearchPage({
  initialQuery,
  initialRawResults,
  initialCuratedTokens,
}: SearchPageProps) {
  const { mode } = useUiMode()
  const router = useRouter()
  const routerQuery = typeof router.query.q === 'string' ? router.query.q : ''
  const [query, setQuery] = useState(initialQuery)
  const [rawResults, setRawResults] = useState<RawExplorerSearchItem[]>(initialRawResults)
  const [loading, setLoading] = useState(false)
  const [hasSearched, setHasSearched] = useState(Boolean(initialQuery.trim()))
  const [error, setError] = useState<string | null>(null)
  const [curatedTokens, setCuratedTokens] = useState<TokenListToken[]>(initialCuratedTokens)
  const [savedQueries, setSavedQueries] = useState<string[]>([])
  const [filterMode, setFilterMode] = useState<SearchFilterMode>('all')
  const [tokenMarkets, setTokenMarkets] = useState<Record<string, TokenAggregationTokenSnapshot>>({})
  const [wrappedRegistryRows, setWrappedRegistryRows] = useState<WrappedTransportTokenRow[]>([])
  const [mappingPairs, setMappingPairs] = useState<TokenMappingPair[]>([])
  const [meshRows, setMeshRows] = useState<MeshCounterpartRow[]>([])
  const [meshLoading, setMeshLoading] = useState(false)
  const [polygonMapper, setPolygonMapper] = useState<PolygonMapperResponse | null>(null)
  const [poolRegistryRows, setPoolRegistryRows] = useState<CuratedPoolRegistryEntry[]>([])
  const [indexedPage, setIndexedPage] = useState(1)
  const [activeSearchTab, setActiveSearchTab] = useState<SearchResultsTab>('indexed')

  const runSearch = async (rawQuery: string) => {
    const trimmedQuery = rawQuery.trim()
    if (!trimmedQuery) {
      setHasSearched(false)
      setRawResults([])
      setError(null)
      return
    }

    setHasSearched(true)
      setLoading(true)
      setError(null)
    try {
      const response = await fetch(
        `/api/v2/search?q=${encodeURIComponent(trimmedQuery)}`
      )
      const data = await response.json().catch(() => null)
      if (!response.ok) {
        setRawResults([])
        setError('Search is temporarily unavailable right now.')
        return
      }
      setRawResults(Array.isArray(data?.items) ? data.items : [])
    } catch (error) {
      console.error('Search failed:', error)
      setRawResults([])
      setError('Search is temporarily unavailable right now.')
    } finally {
      setLoading(false)
    }
  }

  useEffect(() => {
    if (initialCuratedTokens.length > 0) {
      setCuratedTokens(initialCuratedTokens)
      return
    }

    let active = true
    tokensApi.listForSurface('catalog', 138).then(({ ok, data }) => {
      if (active) {
        setCuratedTokens(ok ? data : [])
      }
    }).catch(() => {
      if (active) {
        setCuratedTokens([])
      }
    })
    return () => {
      active = false
    }
  }, [initialCuratedTokens])

  useEffect(() => {
    let active = true
    void fetchCwRegistry()
      .then((chains) => {
        if (!active) return
        setWrappedRegistryRows(flattenCwRegistry(chains))
      })
      .catch(() => {
        if (active) setWrappedRegistryRows([])
      })
    return () => {
      active = false
    }
  }, [])

  useEffect(() => {
    let active = true
    void fetchPoolRegistry(138)
      .then((report) => {
        if (active) setPoolRegistryRows(report?.pools ?? [])
      })
      .catch(() => {
        if (active) setPoolRegistryRows([])
      })
    return () => {
      active = false
    }
  }, [])

  useEffect(() => {
    let active = true
    void fetchTokenMappingPairs()
      .then((pairs) => {
        if (active) setMappingPairs(pairs)
      })
      .catch(() => {
        if (active) setMappingPairs([])
      })
    return () => {
      active = false
    }
  }, [])

  useEffect(() => {
    if (typeof window === 'undefined') return
    try {
      const stored = window.localStorage.getItem('explorer_saved_queries')
      const parsed = stored ? JSON.parse(stored) : []
      setSavedQueries(Array.isArray(parsed) ? parsed.filter((entry): entry is string => typeof entry === 'string') : [])
    } catch {
      setSavedQueries([])
    }
  }, [])

  useEffect(() => {
    if (!router.isReady) return
    if (!routerQuery.trim()) return
    if (routerQuery === initialQuery && initialRawResults.length > 0) {
      setQuery(routerQuery)
      setRawResults(initialRawResults)
      setHasSearched(true)
      return
    }
    setQuery(routerQuery)
    void runSearch(routerQuery)
  }, [initialQuery, initialRawResults, router.isReady, routerQuery])

  const handleSearch = async (e: React.FormEvent) => {
    e.preventDefault()
    const trimmedQuery = query.trim()
    if (!trimmedQuery) {
      return
    }

    setSavedQueries((current) => {
      const next = [trimmedQuery, ...current.filter((entry) => entry.toLowerCase() !== trimmedQuery.toLowerCase())].slice(0, 8)
      if (typeof window !== 'undefined') {
        try {
          window.localStorage.setItem('explorer_saved_queries', JSON.stringify(next))
        } catch {}
      }
      return next
    })

    const wrappedMatches = searchWrappedTransportTokens(trimmedQuery, wrappedRegistryRows)
    const meshSeed = inferMeshSeed(trimmedQuery, curatedTokens, wrappedRegistryRows)

    const tokenTarget = inferTokenSearchTarget(trimmedQuery, curatedTokens)
    if (tokenTarget && !meshSeed) {
      void router.push(tokenTarget.href)
      return
    }

    if (wrappedMatches.length === 1 && wrappedMatches[0].chainId === 138) {
      void router.push(`/tokens/${wrappedMatches[0].address}`)
      return
    }

    const ensRegistryTarget = inferEnsSearchTarget(trimmedQuery)
    if (ensRegistryTarget) {
      void router.push(ensRegistryTarget.href)
      return
    }

    const resolvedEns = await resolveEnsAddress(trimmedQuery)
    if (resolvedEns) {
      void router.push(`/addresses/${resolvedEns}`)
      return
    }

    const directTarget = inferDirectSearchTarget(trimmedQuery)
    if (directTarget && !shouldDeferChain138AddressJump(trimmedQuery, wrappedMatches)) {
      void router.push(directTarget.href)
      return
    }

    void router.replace(
      {
        pathname: router.pathname,
        query: { q: trimmedQuery },
      },
      undefined,
      { shallow: true },
    )
    await runSearch(trimmedQuery)
  }

  const trimmedQuery = query.trim()
  const tokenTarget = inferTokenSearchTarget(trimmedQuery, curatedTokens)
  const meshSeed = useMemo(
    () => inferMeshSeed(trimmedQuery, curatedTokens, wrappedRegistryRows),
    [curatedTokens, trimmedQuery, wrappedRegistryRows],
  )
  const chainNameById = useMemo(() => buildChainNameMap(wrappedRegistryRows), [wrappedRegistryRows])
  const poolMatches = useMemo(
    () => searchCuratedPools(trimmedQuery, poolRegistryRows),
    [poolRegistryRows, trimmedQuery],
  )
  const wrappedMatches = useMemo(
    () => searchWrappedTransportTokens(trimmedQuery, wrappedRegistryRows),
    [trimmedQuery, wrappedRegistryRows],
  )
  const filteredWrappedMatches = useMemo(() => {
    if (filterMode === 'all' || filterMode === 'wrapped') return wrappedMatches
    return []
  }, [filterMode, wrappedMatches])
  const directTarget = inferDirectSearchTarget(trimmedQuery)
  const deferAddressJump = shouldDeferChain138AddressJump(trimmedQuery, wrappedMatches)
  const results = useMemo(
    () => normalizeExplorerSearchResults(trimmedQuery, rawResults, curatedTokens),
    [curatedTokens, rawResults, trimmedQuery],
  )
  const filteredResults = useMemo(() => {
    if (filterMode === 'gru') return results.filter((result) => result.is_gru_token)
    if (filterMode === 'x402') return results.filter((result) => result.is_x402_ready)
    if (filterMode === 'wrapped') return results.filter((result) => result.is_wrapped_transport)
    return results
  }, [filterMode, results])
  const curatedSuggestions = useMemo(
    () => suggestCuratedTokens(trimmedQuery, curatedTokens),
    [curatedTokens, trimmedQuery],
  )
  const groupedResults = useMemo(() => ({
    tokens: filteredResults.filter((result) => result.type === 'token'),
    addresses: filteredResults.filter((result) => result.type === 'address'),
    transactions: filteredResults.filter((result) => result.type === 'transaction'),
    blocks: filteredResults.filter((result) => result.type === 'block'),
    other: filteredResults.filter((result) => !['token', 'address', 'transaction', 'block'].includes(result.type)),
  }), [filteredResults])
  const hasSupplementalMatches = useMemo(
    () =>
      meshRows.length > 0 ||
      poolMatches.length > 0 ||
      filteredWrappedMatches.length > 0 ||
      curatedSuggestions.length > 0 ||
      Boolean(meshSeed),
    [curatedSuggestions.length, filteredWrappedMatches.length, meshRows.length, meshSeed, poolMatches.length],
  )
  const resultSections = [
    { label: 'Tokens', items: groupedResults.tokens },
    { label: 'Addresses', items: groupedResults.addresses },
    { label: 'Transactions', items: groupedResults.transactions },
    { label: 'Blocks', items: groupedResults.blocks },
    { label: 'Other', items: groupedResults.other },
  ]

  const indexedPageCount = Math.max(1, Math.ceil(filteredResults.length / SEARCH_INDEXED_PAGE_SIZE))
  const paginatedFilteredResults = useMemo(() => {
    const safePage = Math.min(Math.max(indexedPage, 1), indexedPageCount)
    const start = (safePage - 1) * SEARCH_INDEXED_PAGE_SIZE
    return filteredResults.slice(start, start + SEARCH_INDEXED_PAGE_SIZE)
  }, [filteredResults, indexedPage, indexedPageCount])
  const paginatedGroupedResults = useMemo(
    () => ({
      tokens: paginatedFilteredResults.filter((result) => result.type === 'token'),
      addresses: paginatedFilteredResults.filter((result) => result.type === 'address'),
      transactions: paginatedFilteredResults.filter((result) => result.type === 'transaction'),
      blocks: paginatedFilteredResults.filter((result) => result.type === 'block'),
      other: paginatedFilteredResults.filter((result) => !['token', 'address', 'transaction', 'block'].includes(result.type)),
    }),
    [paginatedFilteredResults],
  )
  const paginatedResultSections = useMemo(
    () => [
      { label: 'Tokens', items: paginatedGroupedResults.tokens },
      { label: 'Addresses', items: paginatedGroupedResults.addresses },
      { label: 'Transactions', items: paginatedGroupedResults.transactions },
      { label: 'Blocks', items: paginatedGroupedResults.blocks },
      { label: 'Other', items: paginatedGroupedResults.other },
    ],
    [paginatedGroupedResults],
  )
  const searchTabs = useMemo<SectionTab<SearchResultsTab>[]>(() => {
    const tabs: SectionTab<SearchResultsTab>[] = []
    if (filteredResults.length > 0) tabs.push({ id: 'indexed', label: 'Indexed', count: filteredResults.length })
    if (meshSeed && (meshLoading || meshRows.length > 0)) tabs.push({ id: 'mesh', label: 'Mesh', count: meshRows.length })
    if (poolMatches.length > 0 && hasSearched) tabs.push({ id: 'pools', label: 'Pools', count: poolMatches.length })
    if (filteredWrappedMatches.length > 0 && meshRows.length === 0 && !meshLoading) {
      tabs.push({ id: 'wrapped', label: 'Wrapped', count: filteredWrappedMatches.length })
    }
    return tabs
  }, [
    filteredResults.length,
    filteredWrappedMatches.length,
    hasSearched,
    meshLoading,
    meshRows.length,
    meshSeed,
    poolMatches.length,
  ])

  useEffect(() => {
    setIndexedPage(1)
  }, [trimmedQuery, filterMode])

  useEffect(() => {
    if (searchTabs.length === 0) return
    if (!searchTabs.some((tab) => tab.id === activeSearchTab)) {
      setActiveSearchTab(searchTabs[0].id)
    }
  }, [activeSearchTab, searchTabs])

  useEffect(() => {
    if (!meshSeed || !hasSearched) {
      setMeshRows([])
      setMeshLoading(false)
      return
    }

    let active = true
    setMeshLoading(true)

    void (async () => {
      let hubAddress = meshSeed.hubAddress
      if (meshSeed.needsHubResolve && meshSeed.sourceChainId != null && meshSeed.sourceAddress) {
        const toHub = await resolveTokenMapping(meshSeed.sourceChainId, meshSeed.hubChainId, meshSeed.sourceAddress)
        hubAddress = toHub?.addressOnTarget ?? hubAddress
      }

      if (!hubAddress || !/^0x[a-fA-F0-9]{40}$/.test(hubAddress)) {
        if (active) {
          setMeshRows([])
          setMeshLoading(false)
        }
        return
      }

      const destinationChainIds = getMeshDestinationChainIds(mappingPairs, meshSeed.hubChainId)
      const resolvedByChain = await resolveMeshFromHub(meshSeed.hubChainId, hubAddress, destinationChainIds)
      const rows = buildMeshCounterpartRows(meshSeed, resolvedByChain, wrappedRegistryRows, chainNameById)
      if (active) {
        setMeshRows(rows)
        setMeshLoading(false)
      }
    })().catch(() => {
      if (active) {
        setMeshRows([])
        setMeshLoading(false)
      }
    })

    return () => {
      active = false
    }
  }, [chainNameById, hasSearched, mappingPairs, meshSeed, wrappedRegistryRows])

  useEffect(() => {
    if (!meshSeed || !hasSearched) {
      setPolygonMapper(null)
      return
    }
    let active = true
    void fetchPolygonMapper(trimmedQuery)
      .then((report) => {
        if (active) setPolygonMapper(report)
      })
      .catch(() => {
        if (active) setPolygonMapper(null)
      })
    return () => {
      active = false
    }
  }, [hasSearched, meshSeed, trimmedQuery])

  useEffect(() => {
    let active = true

    const tokenAddresses = filteredResults
      .filter((result) => result.type === 'token' && typeof result.data.address === 'string' && result.data.address.trim().length > 0)
      .map((result) => result.data.address as string)

    tokenAggregationApi.getTokensByAddressSafe(138, tokenAddresses).then(({ data }) => {
      if (!active) return
      setTokenMarkets(Object.fromEntries(data.map((snapshot) => [snapshot.address.toLowerCase(), snapshot])))
    }).catch(() => {
      if (active) {
        setTokenMarkets({})
      }
    })

    return () => {
      active = false
    }
  }, [filteredResults])

  return (
    <div className="container mx-auto px-4 py-4 sm:py-6">
      <PageIntro
        compact
        eyebrow="Explorer Lookup"
        title="Search"
        description={
          mode === 'guided'
            ? 'Search by address, transaction, block, token, or mesh/cW* symbols.'
            : 'Search tx / addr / block / token / mesh.'
        }
        actions={[
          { href: '/tokens', label: 'Tokens' },
          { href: '/pools', label: 'Pools' },
        ]}
      />

      <Card className="mb-6">
        <form onSubmit={handleSearch} className="flex flex-col gap-3 sm:flex-row sm:items-center">
          <input
            type="text"
            value={query}
            onChange={(e) => setQuery(e.target.value)}
            placeholder={mode === 'guided' ? 'Search by address, transaction hash, block number...' : 'Search tx / addr / block / token'}
            className="flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-500"
          />
          <button
            type="submit"
            disabled={loading || !trimmedQuery}
            className="w-full rounded-lg bg-primary-600 px-6 py-2 text-white hover:bg-primary-700 disabled:cursor-not-allowed disabled:opacity-50 sm:w-auto"
          >
            {loading ? 'Searching...' : 'Search'}
          </button>
        </form>
      </Card>

      {!loading && error && (
        <Card className="mb-6">
          <p className="text-sm text-gray-600 dark:text-gray-400">
            {meshRows.length > 0 || poolMatches.length > 0 || filteredWrappedMatches.length > 0
              ? 'Indexed Blockscout search is temporarily unavailable. Mesh, pool registry, and wrapped-token matches below are still available.'
              : error}
          </p>
          {meshRows.length === 0 && poolMatches.length === 0 && filteredWrappedMatches.length === 0 ? (
            <div className="mt-4 flex flex-wrap gap-3 text-sm">
              <Link href="/tokens" className="text-primary-600 hover:underline">
                Try token shortcuts →
              </Link>
              <Link href="/addresses" className="text-primary-600 hover:underline">
                Browse addresses →
              </Link>
            </div>
          ) : null}
        </Card>
      )}

      {!loading && hasSearched && searchTabs.length > 0 ? (
        <>
          <CompactMetricBar
            metrics={[
              { label: 'Indexed', value: String(filteredResults.length) },
              { label: 'Mesh', value: String(meshRows.length) },
              { label: 'Pools', value: String(poolMatches.length) },
              { label: 'Wrapped', value: String(filteredWrappedMatches.length) },
            ]}
          />
          <SectionTabs
            tabs={searchTabs}
            activeTab={activeSearchTab}
            onChange={setActiveSearchTab}
            idPrefix="search-results"
            ariaLabel="Search result sections"
            className="-mx-4 mb-4 px-4"
          />
          <TabPanel idPrefix="search-results" tabId="indexed" activeTab={activeSearchTab}>
      {!loading && tokenTarget && !meshSeed && (
        <Card className="mb-6" title="Direct Token Match">
          <p className="text-sm text-gray-600 dark:text-gray-400">
            {mode === 'guided'
              ? 'This matches a curated Chain 138 token, so you can go straight to the token detail page instead of sifting through generic search results.'
              : 'Curated Chain 138 token match.'}
          </p>
          <div className="mt-4">
            <Link href={tokenTarget.href} className="text-primary-600 hover:underline">
              {tokenTarget.label} →
            </Link>
          </div>
        </Card>
      )}

      {!loading && !tokenTarget && directTarget && deferAddressJump && (
        <Card title="Cross-network wrapped token match">
          <p className="mb-4 text-sm text-gray-600 dark:text-gray-400">
            This address is a wrapped transport token on another network — not a Chain 138 address. Open it on the native explorer for that chain.
          </p>
          <div className="space-y-3">
            {wrappedMatches
              .filter((row) => row.matchReason === 'exact address')
              .map((row) => {
                const href = externalChainExplorerUrl(row.chainId, row.address)
                return (
                  <div key={`${row.chainId}-${row.address}`} className="rounded-xl border border-gray-200 px-4 py-3 dark:border-gray-700">
                    <div className="flex flex-wrap items-center gap-2">
                      <EntityBadge label={row.symbol} tone="info" />
                      <EntityBadge label={`chain ${row.chainId}`} tone="neutral" />
                      <EntityBadge label={row.chainName} tone="warning" />
                      <EntityBadge label={row.matchReason} tone="success" className="normal-case tracking-normal" />
                    </div>
                    <div className="mt-2">
                      <Address address={row.address} truncate showCopy />
                    </div>
                    {href ? (
                      <a
                        href={href}
                        target={row.chainId === 138 ? undefined : '_blank'}
                        rel={row.chainId === 138 ? undefined : 'noopener noreferrer'}
                        className="mt-2 inline-block text-sm font-medium text-primary-600 hover:underline"
                      >
                        {row.chainId === 138 ? 'Open on Chain 138 →' : `Open on ${row.chainName} explorer →`}
                      </a>
                    ) : null}
                  </div>
                )
              })}
          </div>
        </Card>
      )}

      {!loading && !tokenTarget && directTarget && !deferAddressJump && (
        <Card className="mb-6" title="Direct Match">
          <p className="text-sm text-gray-600 dark:text-gray-400">
            {mode === 'guided'
              ? 'This looks like a direct explorer identifier. You can open it without waiting for indexed search results.'
              : 'Direct explorer identifier detected.'}
          </p>
          <div className="mt-4">
            <Link href={directTarget.href} className="text-primary-600 hover:underline">
              {directTarget.label} →
            </Link>
          </div>
        </Card>
      )}

      {!loading && !tokenTarget && !directTarget && curatedSuggestions.length > 0 && (
        <Card className="mb-6" title="Curated Suggestions">
          <div className="space-y-3">
            <p className="text-sm text-gray-600 dark:text-gray-400">
              These listed Chain 138 assets are close to your query, which is often more useful than relying on a generic explorer result set alone.
            </p>
            <div className="flex flex-wrap gap-3">
              {curatedSuggestions.map((token) => (
                <Link key={token.address} href={`/tokens/${token.address}`} className="inline-flex items-center gap-2 rounded-full border border-gray-200 px-3 py-2 text-sm text-primary-600 hover:border-primary-300 hover:underline dark:border-gray-700">
                  <EntityBadge label="listed" tone="success" />
                  <span>{token.symbol || token.name || token.address}</span>
                </Link>
              ))}
            </div>
          </div>
        </Card>
      )}

      {filteredResults.length > 0 && (
        <Card title="Indexed search results">
          <div className="space-y-6">
            <div className="flex flex-wrap items-center gap-2">
              {([
                ['all', 'All results'],
                ['gru', 'GRU'],
                ['x402', 'x402 ready'],
                ['wrapped', 'Wrapped'],
              ] as const).map(([mode, label]) => (
                <button
                  key={mode}
                  type="button"
                  onClick={() => setFilterMode(mode)}
                  className={`rounded-full border px-3 py-1.5 text-sm transition ${
                    filterMode === mode
                      ? 'border-primary-500 bg-primary-50 text-primary-700 dark:border-primary-400 dark:bg-primary-950/40 dark:text-primary-200'
                      : 'border-gray-200 text-gray-600 hover:border-primary-300 hover:text-primary-600 dark:border-gray-700 dark:text-gray-300'
                  }`}
                >
                  {label}
                </button>
              ))}
              <Link href="/docs/gru" className="ml-auto text-sm text-primary-600 hover:underline">
                GRU guide →
              </Link>
            </div>
            {paginatedResultSections.map((section) =>
              section.items.length > 0 ? (
                <div key={section.label}>
                  <div className="mb-3 text-xs font-semibold uppercase tracking-[0.2em] text-gray-500 dark:text-gray-400">
                    {section.label}
                  </div>
                  <div className="space-y-4">
                    {section.items.map((result, index) => (
              <div key={result.data?.hash ?? result.data?.address ?? result.data?.number ?? index} className="border-b border-gray-200 pb-4 last:border-0 dark:border-gray-700">
                {result.type === 'block' && result.data.number && (
                  <Link href={result.href || `/blocks/${result.data.number}`} className="inline-flex flex-col gap-2 text-primary-600 hover:underline">
                    <div className="flex flex-wrap items-center gap-2">
                      <EntityBadge label="block" tone="neutral" />
                    </div>
                    <span className="font-medium text-gray-900 dark:text-white">Block #{result.data.number}</span>
                  </Link>
                )}
                {result.type === 'transaction' && result.data.hash && (
                  <Link href={result.href || `/transactions/${result.data.hash}`} className="inline-flex flex-col gap-2 text-primary-600 hover:underline">
                    <div className="flex flex-wrap items-center gap-2">
                      <EntityBadge label="transaction" tone="neutral" />
                    </div>
                    <Address address={result.data.hash} truncate showCopy={false} />
                  </Link>
                )}
                {(result.type === 'address' || result.type === 'token') && result.data.address && (
                  (() => {
                    const market = result.type === 'token'
                      ? tokenMarkets[result.data.address.toLowerCase()]?.market
                      : null
                    return (
                      <Link
                        href={result.href || (result.type === 'token' ? `/tokens/${result.data.address}` : `/addresses/${result.data.address}`)}
                        className="inline-flex flex-col gap-2 text-primary-600 hover:underline"
                      >
                        <div className="flex flex-wrap items-center gap-2">
                          <EntityBadge
                            label={result.type === 'token' ? 'token' : 'address'}
                            tone={result.type === 'token' ? 'success' : 'neutral'}
                          />
                          {result.symbol && <EntityBadge label={result.symbol} tone="info" />}
                          {result.token_type && <EntityBadge label={result.token_type} tone="warning" />}
                          {result.is_curated_token && <EntityBadge label="listed" tone="success" />}
                          {result.is_gru_token && <EntityBadge label="GRU" tone="success" />}
                          {result.is_x402_ready && <PostureBadge label="x402 ready" tone="info" />}
                          {result.is_wrapped_transport && <EntityBadge label="cW public-network" tone="warning" />}
                          {result.currency_code ? <EntityBadge label={result.currency_code} tone="neutral" /> : null}
                          {result.match_reason ? <EntityBadge label={result.match_reason} tone="info" className="normal-case tracking-normal" /> : null}
                          {result.matched_tags?.map((tag) => <EntityBadge key={tag} label={tag} />)}
                        </div>
                        <span className="font-medium text-gray-900 dark:text-white">
                          {result.name || result.symbol || result.label}
                        </span>
                        <Address address={result.data.address} truncate showCopy={false} />
                        {market ? (
                          <div className="text-sm text-gray-500 dark:text-gray-400">
                            <div className="flex flex-wrap items-baseline gap-x-3 gap-y-1">
                              <span>Live price: {formatUsd(market.priceUsd)}</span>
                              <TokenLiquidityStack market={market} className="space-y-0.5 text-sm text-gray-500 dark:text-gray-400" />
                            </div>
                            <MarketEvidenceNote lastUpdated={market.lastUpdated} compact />
                          </div>
                        ) : null}
                      </Link>
                    )
                  })()
                )}
                <div className="mt-2 flex flex-wrap gap-x-3 gap-y-1 text-sm text-gray-500">
                  <span>Type: {result.type}</span>
                  <span>Chain: {result.chain_id ?? 138}</span>
                  <span>Priority: {(result.score ?? 0).toFixed(2)}</span>
                </div>
              </div>
                    ))}
                  </div>
                </div>
              ) : null,
            )}
            <PaginationControls
              page={indexedPage}
              pageCount={indexedPageCount}
              onPageChange={setIndexedPage}
              label="Indexed results"
              ariaLabel="Indexed search pagination"
            />
          </div>
        </Card>
      )}

      {!loading && hasSearched && hasSupplementalMatches && filteredResults.length === 0 && !error ? (
        <Card className="mb-4" title="Registry matches">
          <p className="text-sm text-gray-600 dark:text-gray-400">
            Blockscout indexed search returned no hits for{' '}
            <span className="font-medium text-gray-900 dark:text-white">{trimmedQuery}</span>, but curated mesh,
            pool, or wrapped-token registry matches are shown in the other tabs.
          </p>
        </Card>
      ) : null}
          </TabPanel>

          <TabPanel idPrefix="search-results" tabId="pools" activeTab={activeSearchTab}>
        <Card title="Curated liquidity pools">
          <p className="mb-4 text-sm text-gray-600 dark:text-gray-400">
            Matches from the DODO PMM + UniV2 pool registry on Chain 138. LP receipt tokens stay chain-local — bridge
            underlying c*/cW* instead.
          </p>
          <div className="space-y-3">
            {poolMatches.slice(0, 6).map((row) => (
              <Link
                key={`${row.poolAddress}-${row.matchReason}`}
                href={`/pools/${row.poolAddress}`}
                className="block rounded-xl border border-gray-200 px-4 py-3 hover:border-primary-300 dark:border-gray-700"
              >
                <div className="flex flex-wrap items-center gap-2">
                  <EntityBadge label={row.pairLabel} tone="info" />
                  <EntityBadge label={row.venue.replace('_', ' ')} tone="neutral" />
                  <EntityBadge label={row.matchReason} tone="success" className="normal-case tracking-normal" />
                </div>
                <div className="mt-2 text-xs text-gray-500 dark:text-gray-400">
                  Pool <Address address={row.poolAddress} truncate showCopy={false} />
                </div>
              </Link>
            ))}
          </div>
        </Card>
          </TabPanel>

          <TabPanel idPrefix="search-results" tabId="mesh" activeTab={activeSearchTab}>
        <Card title="Mesh counterparts">
          <p className="mb-4 text-sm text-gray-600 dark:text-gray-400">
            {mode === 'guided'
              ? `Resolved ${meshSeed?.hubSymbol} hub on Chain 138 and mapped ${meshSeed?.wrappedSymbol} addresses across configured transport pairs via token-mapping.`
              : `${meshSeed?.hubSymbol} ↔ ${meshSeed?.wrappedSymbol} mesh (${meshSeed?.matchReason}).`}
          </p>
          {polygonMapper?.officialPolygonTokenList?.upstreamRepo ? (
            <p className="mb-4 text-xs text-gray-500 dark:text-gray-400">
              Polygon (137) rows align with{' '}
              <a
                href={polygonMapper.officialPolygonTokenList.submissionDoc || polygonMapper.officialPolygonTokenList.upstreamRepo}
                target="_blank"
                rel="noopener noreferrer"
                className="text-primary-600 hover:underline"
              >
                official Polygon Token Mapper
              </a>
              {polygonMapper.match?.polygonAddress
                ? ` — matched ${polygonMapper.match.wrappedSymbol} at ${polygonMapper.match.polygonAddress.slice(0, 10)}…`
                : ''}
              .
            </p>
          ) : null}
          {meshLoading ? (
            <p className="text-sm text-gray-500 dark:text-gray-400">Resolving mesh addresses…</p>
          ) : (
            <div className="space-y-3">
              {meshRows.map((row) => {
                const href = externalChainExplorerUrl(row.chainId, row.address)
                const content = (
                  <>
                    <div className="flex flex-wrap items-center gap-2">
                      <EntityBadge label={row.symbol} tone="info" />
                      <EntityBadge label={`chain ${row.chainId}`} tone="neutral" />
                      <EntityBadge label={row.chainName} tone="warning" />
                      <EntityBadge
                        label={row.role === 'hub' ? 'hub (138)' : row.mappedVia === 'token-mapping' ? 'mapped' : 'registry'}
                        tone={row.role === 'hub' ? 'success' : 'info'}
                        className="normal-case tracking-normal"
                      />
                      {row.chainId === 137 && polygonMapper ? (
                        <EntityBadge label="Polygon mapper" tone="warning" className="normal-case tracking-normal" />
                      ) : null}
                    </div>
                    <div className="mt-2">
                      <Address address={row.address} truncate showCopy />
                    </div>
                  </>
                )
                if (!href) {
                  return (
                    <div key={`mesh-${row.chainId}-${row.address}`} className="rounded-xl border border-gray-200 px-4 py-3 dark:border-gray-700">
                      {content}
                    </div>
                  )
                }
                if (row.chainId === 138) {
                  return (
                    <Link
                      key={`mesh-${row.chainId}-${row.address}`}
                      href={href}
                      className="block rounded-xl border border-gray-200 px-4 py-3 text-primary-600 hover:border-primary-300 dark:border-gray-700"
                    >
                      {content}
                    </Link>
                  )
                }
                return (
                  <a
                    key={`mesh-${row.chainId}-${row.address}`}
                    href={href}
                    target="_blank"
                    rel="noopener noreferrer"
                    className="block rounded-xl border border-gray-200 px-4 py-3 text-primary-600 hover:border-primary-300 dark:border-gray-700"
                  >
                    {content}
                  </a>
                )
              })}
            </div>
          )}
        </Card>
          </TabPanel>

          <TabPanel idPrefix="search-results" tabId="wrapped" activeTab={activeSearchTab}>
        <Card title="Wrapped transport tokens (cross-network)">
          <p className="mb-4 text-sm text-gray-600 dark:text-gray-400">
            Matches from the live cW* registry across public networks. Chain 138 tokens open here; other chains link to native explorers.
          </p>
          <div className="space-y-3">
            {filteredWrappedMatches.slice(0, 8).map((row) => {
              const href = externalChainExplorerUrl(row.chainId, row.address)
              const content = (
                <>
                  <div className="flex flex-wrap items-center gap-2">
                    <EntityBadge label={row.symbol} tone="info" />
                    <EntityBadge label={`chain ${row.chainId}`} tone="neutral" />
                    <EntityBadge label={row.chainName} tone="warning" />
                    <EntityBadge label="cW registry" tone="success" />
                    <EntityBadge label={row.matchReason} tone="info" className="normal-case tracking-normal" />
                  </div>
                  <div className="mt-2">
                    <Address address={row.address} truncate showCopy />
                  </div>
                </>
              )
              if (!href) {
                return (
                  <div key={`${row.chainId}-${row.address}`} className="rounded-xl border border-gray-200 px-4 py-3 dark:border-gray-700">
                    {content}
                  </div>
                )
              }
              if (row.chainId === 138) {
                return (
                  <Link
                    key={`${row.chainId}-${row.address}`}
                    href={href}
                    className="block rounded-xl border border-gray-200 px-4 py-3 text-primary-600 hover:underline dark:border-gray-700"
                  >
                    {content}
                  </Link>
                )
              }
              return (
                <a
                  key={`${row.chainId}-${row.address}`}
                  href={href}
                  target="_blank"
                  rel="noopener noreferrer"
                  className="block rounded-xl border border-gray-200 px-4 py-3 text-primary-600 hover:underline dark:border-gray-700"
                >
                  {content}
                </a>
              )
            })}
          </div>
        </Card>
          </TabPanel>
        </>
      ) : null}

      {!loading && hasSearched && !error && filteredResults.length === 0 && !hasSupplementalMatches && (
        <Card title="No Results Found">
          <p className="text-sm text-gray-600 dark:text-gray-400">
            No explorer results matched <span className="font-medium text-gray-900 dark:text-white">{trimmedQuery}</span>
            {filterMode !== 'all' ? ` in the current ${filterMode} filter` : ''}.
            Try a full address, transaction hash, token symbol, or block number.
          </p>
          <div className="mt-4 flex flex-wrap gap-3 text-sm">
            <Link href="/tokens" className="text-primary-600 hover:underline">
              Token shortcuts →
            </Link>
            <Link href="/watchlist" className="text-primary-600 hover:underline">
              Watchlist →
            </Link>
            <Link href="/docs/gru" className="text-primary-600 hover:underline">
              GRU guide →
            </Link>
          </div>
        </Card>
      )}

      {!loading && !hasSearched && (
        <DisclosureSection title="Popular starting points" defaultOpen={false} alwaysCollapsible>
        <Card title="Popular Starting Points">
          <div className="space-y-4">
            <div className="flex flex-wrap gap-3 text-sm">
              <Link href="/search?q=cUSDT" className="text-primary-600 hover:underline">
                cUSDT →
              </Link>
              <Link href="/search?q=cUSDC" className="text-primary-600 hover:underline">
                cUSDC →
              </Link>
              <Link href="/search?q=cXAUC" className="text-primary-600 hover:underline">
                cXAUC →
              </Link>
              <Link href="/search?q=USDT" className="text-primary-600 hover:underline">
                USDT →
              </Link>
            </div>
            {savedQueries.length > 0 ? (
              <div>
                <div className="mb-2 text-xs font-semibold uppercase tracking-[0.2em] text-gray-500 dark:text-gray-400">
                  Recent Searches
                </div>
                <div className="flex flex-wrap gap-3 text-sm">
                  {savedQueries.map((savedQuery) => (
                    <Link key={savedQuery} href={`/search?q=${encodeURIComponent(savedQuery)}`} className="text-primary-600 hover:underline">
                      {savedQuery} →
                    </Link>
                  ))}
                </div>
              </div>
            ) : null}
          </div>
        </Card>
        </DisclosureSection>
      )}
    </div>
  )
}

export const getServerSideProps: GetServerSideProps<SearchPageProps> = async (context) => {
  const initialQuery = typeof context.query.q === 'string' ? context.query.q.trim() : ''
  const { tokens: initialCuratedTokens } = await fetchTokenListForSurface('catalog', 138)

  const tokenTarget = initialQuery ? inferTokenSearchTarget(initialQuery, initialCuratedTokens) : null
  if (tokenTarget) {
    return { redirect: { destination: tokenTarget.href, permanent: false } }
  }

  const directTarget = initialQuery ? inferDirectSearchTarget(initialQuery) : null
  if (directTarget?.kind === 'address') {
    return { redirect: { destination: directTarget.href, permanent: false } }
  }

  if (initialQuery && isEnsName(initialQuery)) {
    const resolved = await resolveEnsAddress(initialQuery)
    if (resolved) {
      return { redirect: { destination: `/addresses/${resolved}`, permanent: false } }
    }
  }

  return {
    props: {
      initialQuery,
      initialRawResults: [],
      initialCuratedTokens,
    },
  }
}
