import type { GetStaticProps } from 'next'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { Card } from '@/libs/frontend-ui-primitives'
import PageIntro from '@/components/common/PageIntro'
import EntityBadge from '@/components/common/EntityBadge'
import ListFilterBar from '@/components/common/ListFilterBar'
import MarketEvidenceNote from '@/components/common/MarketEvidenceNote'
import PaginationControls from '@/components/common/PaginationControls'
import SectionTabs, { TabPanel, type SectionTab } from '@/components/common/SectionTabs'
import { tokensApi, type IndexedTokenListItem } from '@/services/api/tokens'
import type { TokenListToken } from '@/services/api/config'
import {
  buildCanonicalAddressSet,
  isCanonicalTokenAddress,
  sortIndexedTokensCanonicalFirst,
} from '@/utils/canonicalTokens'
import { tokenAggregationApi, type TokenAggregationTokenSnapshot } from '@/services/api/tokenAggregation'
import TokenLiquidityStack from '@/components/wallet/TokenLiquidityStack'
import { fetchTokenListForSurface } from '@/services/api/tokenListSurfaces'
import { selectCuratedFeaturedTokens } from '@/utils/featuredTokens'
import { formatInteger } from '@/utils/format'

const quickSearches = [
  { label: 'cUSDT', description: 'Canonical compliant USD treasury / government bond liquidity and address results.' },
  { label: 'cUSDC', description: 'Canonical compliant USD cash electronic-money routes and address coverage.' },
  { label: 'cXAUC', description: 'Gold-backed cXAUC pools and token references.' },
  { label: 'cXAUT', description: 'Gold-backed cXAUT references and search coverage.' },
  { label: 'cEURT', description: 'EUR liquidity and cXAUC-connected route coverage.' },
  { label: 'USDT', description: 'Native-side USDT routes and address discovery.' },
]

const CURATED_PAGE_SIZE = 6
const INDEXED_PAGE_SIZE = 12

type TokensPageTab = 'curated' | 'indexed' | 'shortcuts'

function normalizeAddress(value: string) {
  const trimmed = value.trim()
  return /^0x[a-fA-F0-9]{40}$/.test(trimmed) ? trimmed : ''
}

function matchesTokenQuery(token: { symbol?: string | null; name?: string | null; address?: string | null }, query: string) {
  const normalizedQuery = query.trim().toLowerCase()
  if (!normalizedQuery) return true
  const haystack = [token.symbol, token.name, token.address].filter(Boolean).join(' ').toLowerCase()
  return haystack.includes(normalizedQuery)
}

interface TokensPageProps {
  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)
}

function tagPriority(tag: string) {
  const order: Record<string, number> = {
    gru: 0,
    compliant: 1,
    'treasury-bond': 2,
    'electronic-money': 2,
    commodity: 2,
    'reference-asset': 0,
    defi: 4,
  }
  return order[tag] ?? 3
}

export default function TokensPage({ initialCuratedTokens }: TokensPageProps) {
  const router = useRouter()
  const [lookupQuery, setLookupQuery] = useState('')
  const [listQuery, setListQuery] = useState('')
  const [activeTab, setActiveTab] = useState<TokensPageTab>('curated')
  const [curatedPage, setCuratedPage] = useState(1)
  const [indexedPage, setIndexedPage] = useState(1)
  const [curatedTokens, setCuratedTokens] = useState<TokenListToken[]>(initialCuratedTokens)
  const [indexedTokens, setIndexedTokens] = useState<IndexedTokenListItem[]>([])
  const [indexedLoading, setIndexedLoading] = useState(true)
  const [featuredMarkets, setFeaturedMarkets] = useState<Record<string, TokenAggregationTokenSnapshot>>({})

  const handleSubmit = (event: React.FormEvent) => {
    event.preventDefault()
    const normalized = normalizeAddress(lookupQuery)
    router.push(normalized ? `/tokens/${normalized}` : `/search?q=${encodeURIComponent(lookupQuery.trim())}`)
  }

  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])

  const featuredCuratedTokens = useMemo(
    () => selectCuratedFeaturedTokens(curatedTokens) as TokenListToken[],
    [curatedTokens],
  )

  const canonicalAddressSet = useMemo(() => buildCanonicalAddressSet(curatedTokens), [curatedTokens])

  const sortedIndexedTokens = useMemo(
    () => sortIndexedTokensCanonicalFirst(indexedTokens, canonicalAddressSet),
    [canonicalAddressSet, indexedTokens],
  )

  const filteredCuratedTokens = useMemo(
    () => featuredCuratedTokens.filter((token) => matchesTokenQuery(token, listQuery)),
    [featuredCuratedTokens, listQuery],
  )

  const filteredIndexedTokens = useMemo(
    () => sortedIndexedTokens.filter((token) => matchesTokenQuery(token, listQuery)),
    [listQuery, sortedIndexedTokens],
  )

  const curatedPageCount = Math.max(1, Math.ceil(filteredCuratedTokens.length / CURATED_PAGE_SIZE))
  const paginatedCuratedTokens = useMemo(() => {
    const safePage = Math.min(Math.max(curatedPage, 1), curatedPageCount)
    const start = (safePage - 1) * CURATED_PAGE_SIZE
    return filteredCuratedTokens.slice(start, start + CURATED_PAGE_SIZE)
  }, [curatedPage, curatedPageCount, filteredCuratedTokens])

  const loadIndexedTokens = useCallback(async (page: number) => {
    setIndexedLoading(true)
    try {
      const { ok, data } = await tokensApi.listIndexedSafe(page, INDEXED_PAGE_SIZE)
      setIndexedTokens(ok ? data : [])
    } catch {
      setIndexedTokens([])
    } finally {
      setIndexedLoading(false)
    }
  }, [])

  useEffect(() => {
    void loadIndexedTokens(indexedPage)
  }, [indexedPage, loadIndexedTokens])

  useEffect(() => {
    setCuratedPage(1)
    setIndexedPage(1)
  }, [listQuery])

  useEffect(() => {
    if (curatedPage > curatedPageCount) {
      setCuratedPage(curatedPageCount)
    }
  }, [curatedPage, curatedPageCount])

  useEffect(() => {
    let active = true

    const featuredAddresses = featuredCuratedTokens
      .map((token) => token.address)
      .filter((address): address is string => typeof address === 'string' && address.trim().length > 0)

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

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

  const tokenTabs = useMemo<SectionTab<TokensPageTab>[]>(
    () => [
      { id: 'curated', label: 'Curated', count: featuredCuratedTokens.length },
      { id: 'indexed', label: 'Indexed', count: sortedIndexedTokens.length },
      { id: 'shortcuts', label: 'Quick searches', count: quickSearches.length },
    ],
    [featuredCuratedTokens.length, sortedIndexedTokens.length],
  )

  const indexedHasNextPage = indexedTokens.length === INDEXED_PAGE_SIZE

  return (
    <div className="container mx-auto px-4 py-5 sm:py-6">
      <PageIntro
        compact
        eyebrow="Token Discovery"
        title="Tokens"
        description="Browse canonical Chain 138 tokens, open contracts, and review holders, transfers, and liquidity."
        actions={[
          { href: '/wallet', label: 'Wallet tools' },
          { href: '/search', label: 'Search' },
        ]}
      />

      <SectionTabs
        tabs={tokenTabs}
        activeTab={activeTab}
        onChange={setActiveTab}
        idPrefix="tokens-page"
        ariaLabel="Token list sections"
        className="-mx-4 mb-4 px-4"
      />

      <TabPanel idPrefix="tokens-page" tabId="curated" activeTab={activeTab}>
        <Card title="Find a token">
          <form onSubmit={handleSubmit} className="flex flex-col gap-2 sm:flex-row">
            <input
              type="text"
              value={lookupQuery}
              onChange={(event) => setLookupQuery(event.target.value)}
              placeholder="Token symbol, name, or contract address"
              className="min-h-10 flex-1 rounded-lg border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary-500 dark:border-gray-700 dark:bg-gray-950"
            />
            <button
              type="submit"
              disabled={!lookupQuery.trim()}
              className="min-h-10 rounded-lg bg-primary-600 px-4 py-2 text-sm font-semibold text-white hover:bg-primary-700 disabled:cursor-not-allowed disabled:opacity-50"
            >
              Search
            </button>
          </form>
        </Card>
        <Card className="mt-4" title="Curated Chain 138 tokens">
          <ListFilterBar
            query={listQuery}
            onQueryChange={setListQuery}
            placeholder="Filter curated tokens by symbol, name, or address"
            resultCount={filteredCuratedTokens.length}
            totalCount={featuredCuratedTokens.length}
            className="mb-4"
          />
          <div className="mt-4 grid gap-3 md:grid-cols-2 xl:grid-cols-3">
            {paginatedCuratedTokens.length === 0 ? (
              <p className="text-sm text-gray-600 dark:text-gray-400 md:col-span-2 xl:col-span-3">
                No curated tokens match your filter.
              </p>
            ) : (
              paginatedCuratedTokens
                .filter((token): token is TokenListToken & { address: string } => typeof token.address === 'string' && token.address.trim().length > 0)
                .map((token) => {
                  const market = featuredMarkets[token.address.toLowerCase()]?.market
                  return (
                    <Link
                      key={token.address}
                      href={`/tokens/${token.address}`}
                      className="rounded-lg border border-gray-200 p-4 transition hover:border-primary-400 hover:bg-gray-50 dark:border-gray-700 dark:hover:bg-gray-950/50"
                    >
                      <div className="flex items-start justify-between gap-3">
                        <div className="min-w-0">
                          <div className="text-sm font-semibold text-gray-900 dark:text-white">{token.symbol || token.name || 'Token'}</div>
                          <p className="mt-1 line-clamp-2 text-sm leading-5 text-gray-600 dark:text-gray-400">
                            {token.name || 'Listed in the Chain 138 token registry.'}
                          </p>
                        </div>
                      </div>
                      {market ? (
                        <div className="mt-3 grid grid-cols-2 gap-2 text-sm text-gray-700 dark:text-gray-300">
                          <div className="rounded-lg bg-gray-50 px-3 py-2 dark:bg-gray-950/60">
                            <div className="text-[11px] font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Price</div>
                            <div className="mt-1 font-semibold text-gray-950 dark:text-white">{formatUsd(market.priceUsd)}</div>
                          </div>
                          <div className="rounded-lg bg-gray-50 px-3 py-2 dark:bg-gray-950/60">
                            <div className="text-[11px] font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">Cash &amp; reserve</div>
                            <div className="mt-1">
                              <TokenLiquidityStack market={market} />
                            </div>
                          </div>
                          <div className="col-span-2">
                            <MarketEvidenceNote lastUpdated={market.lastUpdated} compact />
                          </div>
                        </div>
                      ) : null}
                      {token.tags && token.tags.length > 0 ? (
                        <div className="mt-3 flex flex-wrap gap-2">
                          {[...token.tags].sort((a, b) => tagPriority(a) - tagPriority(b)).slice(0, 3).map((tag) => (
                            <EntityBadge key={`${token.address}-${tag}`} label={tag} className="px-2 py-1 text-[11px]" />
                          ))}
                        </div>
                      ) : null}
                    </Link>
                  )
                })
            )}
          </div>
          <PaginationControls
            page={curatedPage}
            pageCount={curatedPageCount}
            onPageChange={setCuratedPage}
            label="Curated tokens"
            ariaLabel="Curated token pagination"
          />
        </Card>
      </TabPanel>

      <TabPanel idPrefix="tokens-page" tabId="indexed" activeTab={activeTab}>
        <Card title="Indexed tokens (Blockscout)">
          <ListFilterBar
            query={listQuery}
            onQueryChange={setListQuery}
            placeholder="Filter indexed tokens on this page"
            resultCount={filteredIndexedTokens.length}
            totalCount={sortedIndexedTokens.length}
          />
          {indexedLoading ? (
            <p className="mt-4 text-sm text-gray-600 dark:text-gray-400">Loading indexed token feed…</p>
          ) : filteredIndexedTokens.length === 0 ? (
            <p className="mt-4 text-sm text-gray-600 dark:text-gray-400">
              {sortedIndexedTokens.length === 0
                ? 'Indexed token feed is temporarily unavailable.'
                : 'No indexed tokens on this page match your filter.'}
            </p>
          ) : (
            <div className="mt-4 grid gap-2">
              {filteredIndexedTokens.map((token) => {
                const canonical = isCanonicalTokenAddress(token.address, canonicalAddressSet)
                return (
                  <Link
                    key={token.address}
                    href={`/tokens/${token.address}`}
                    className="flex flex-col gap-2 rounded-lg border border-gray-200 px-3 py-3 transition hover:border-primary-400 hover:bg-gray-50 dark:border-gray-700 dark:hover:bg-gray-950/50 sm:flex-row sm:items-center sm:justify-between"
                  >
                    <div className="min-w-0">
                      <div className="flex flex-wrap items-center gap-2">
                        <span className="text-sm font-semibold text-gray-900 dark:text-white">
                          {token.symbol || token.name || 'Token'}
                        </span>
                        {canonical ? (
                          <EntityBadge label="canonical" tone="success" className="px-2 py-0.5 text-[11px]" />
                        ) : (
                          <EntityBadge label="non-canonical indexed" tone="warning" className="px-2 py-0.5 text-[11px]" />
                        )}
                      </div>
                      <p className="mt-1 truncate text-xs text-gray-600 dark:text-gray-400">{token.name || token.address}</p>
                    </div>
                    <div className="text-xs text-gray-500 dark:text-gray-400">
                      {token.holders != null ? `${formatInteger(token.holders)} holders` : 'Holders unavailable'}
                    </div>
                  </Link>
                )
              })}
            </div>
          )}
          <PaginationControls
            page={indexedPage}
            onPageChange={setIndexedPage}
            label="Indexed tokens"
            ariaLabel="Indexed token pagination"
            disabled={indexedLoading}
            hasNextPage={indexedHasNextPage}
          />
        </Card>
      </TabPanel>

      <TabPanel idPrefix="tokens-page" tabId="shortcuts" activeTab={activeTab}>
        <Card title="Common token searches">
          <div className="grid gap-2 sm:grid-cols-2 lg:grid-cols-3">
            {quickSearches.map((token) => (
              <Link
                key={token.label}
                href={`/search?q=${encodeURIComponent(token.label)}`}
                className="rounded-lg border border-gray-200 px-3 py-2 transition hover:border-primary-400 hover:bg-gray-50 dark:border-gray-700 dark:hover:bg-gray-950/50"
              >
                <div className="text-sm font-semibold text-gray-900 dark:text-white">{token.label}</div>
                <p className="mt-1 line-clamp-2 text-xs leading-5 text-gray-600 dark:text-gray-400">{token.description}</p>
              </Link>
            ))}
          </div>
        </Card>
      </TabPanel>
    </div>
  )
}

export const getStaticProps: GetStaticProps<TokensPageProps> = async () => {
  const { tokens } = await fetchTokenListForSurface('catalog', 138)

  return {
    props: {
      initialCuratedTokens: tokens,
    },
    revalidate: 300,
  }
}
