import React, { useMemo, useState } from 'react';
import { createRoot } from 'react-dom/client';
import { Contract, JsonRpcProvider, formatEther, formatUnits, isAddress } from 'ethers';
import { EthereumProvider } from '@walletconnect/ethereum-provider';
import './styles.css';

declare global { interface Window { ethereum?: any } }

type WalletConnectProvider = Awaited<ReturnType<typeof EthereumProvider.init>>;
let walletConnectProvider: WalletConnectProvider | null = null;

type Network = { id:number; name:string; native:string; rpc:string; explorer:string; tokenApi:string };
type Asset = { key:string; network:string; chainId:number; symbol:string; name?:string; amount:string; type:'native'|'token'; tokenAddress?:string; filtered?:boolean; filterReason?:string };

const NETWORKS:Network[] = [
 {id:1,name:'Ethereum',native:'ETH',rpc:'https://ethereum-rpc.publicnode.com',explorer:'https://etherscan.io/address/',tokenApi:'https://eth.blockscout.com/api/v2/addresses/{address}/tokens'},
 {id:10,name:'OP Mainnet',native:'ETH',rpc:'https://optimism-rpc.publicnode.com',explorer:'https://optimistic.etherscan.io/address/',tokenApi:'https://optimism.blockscout.com/api/v2/addresses/{address}/tokens'},
 {id:56,name:'BNB Smart Chain',native:'BNB',rpc:'https://bsc-rpc.publicnode.com',explorer:'https://bscscan.com/address/',tokenApi:'https://bsc.blockscout.com/api/v2/addresses/{address}/tokens'},
 {id:137,name:'Polygon',native:'POL',rpc:'https://polygon-bor-rpc.publicnode.com',explorer:'https://polygonscan.com/address/',tokenApi:'https://polygon.blockscout.com/api/v2/addresses/{address}/tokens'},
 {id:42161,name:'Arbitrum One',native:'ETH',rpc:'https://arbitrum-one-rpc.publicnode.com',explorer:'https://arbiscan.io/address/',tokenApi:'https://arbitrum.blockscout.com/api/v2/addresses/{address}/tokens'},
 {id:43114,name:'Avalanche C-Chain',native:'AVAX',rpc:'https://avalanche-c-chain.publicnode.com',explorer:'https://snowtrace.io/address/',tokenApi:'https://avax.blockscout.com/api/v2/addresses/{address}/tokens'},
 {id:8453,name:'Base',native:'ETH',rpc:'https://base-rpc.publicnode.com',explorer:'https://basescan.org/address/',tokenApi:'https://base.blockscout.com/api/v2/addresses/{address}/tokens'}
];

const FALLBACK_TOKENS:Record<number,{symbol:string;name:string;address:string;decimals:number}[]> = {
 1:[
  {symbol:'USDT',name:'Tether USD',address:'0xdAC17F958D2ee523a2206206994597C13D831ec7',decimals:6},{symbol:'USDC',name:'USD Coin',address:'0xA0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',decimals:6},{symbol:'SHIB',name:'Shiba Inu',address:'0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE',decimals:18},{symbol:'LINK',name:'Chainlink',address:'0x514910771AF9Ca656af840dff83E8264EcF986CA',decimals:18},{symbol:'UNI',name:'Uniswap',address:'0x1f9840a85d5aF5bf1D1762F925BDaddC4201F984',decimals:18},{symbol:'WETH',name:'Wrapped Ether',address:'0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',decimals:18}],
 10:[{symbol:'USDT',name:'Tether USD',address:'0x94b008aA00579c1307B0EF2c499ad98a8ce58e58',decimals:6},{symbol:'USDC',name:'USD Coin',address:'0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85',decimals:6},{symbol:'WETH',name:'Wrapped Ether',address:'0x4200000000000000000000000000000000000006',decimals:18}],
 56:[{symbol:'USDT',name:'Tether USD',address:'0x55d398326f99059fF775485246999027B3197955',decimals:18},{symbol:'USDC',name:'USD Coin',address:'0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d',decimals:18},{symbol:'WBNB',name:'Wrapped BNB',address:'0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c',decimals:18}],
 137:[{symbol:'USDT',name:'Tether USD',address:'0xc2132D05D31c914a87C6611C10748AaCbA0A3C3C',decimals:6},{symbol:'USDC',name:'USD Coin',address:'0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359',decimals:6},{symbol:'WETH',name:'Wrapped Ether',address:'0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619',decimals:18}],
 42161:[{symbol:'USDT',name:'Tether USD',address:'0xFd086bC7CD5C481dcc9C85ebe478A1C0b69FCbb9',decimals:6},{symbol:'USDC',name:'USD Coin',address:'0xaf88d065e77c8cC2239327C5EDb3A432268e5831',decimals:6},{symbol:'WETH',name:'Wrapped Ether',address:'0x82aF49447D8a07e3bd95BD0d56f35241523fBab1',decimals:18}],
 43114:[{symbol:'USDT',name:'Tether USD',address:'0x9702230A8Ea53601f5cD2dc00fDBC13d4dF4A8c7',decimals:6},{symbol:'USDC',name:'USD Coin',address:'0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E',decimals:6},{symbol:'WAVAX',name:'Wrapped AVAX',address:'0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7',decimals:18}],
 8453:[{symbol:'USDT',name:'Tether USD',address:'0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2',decimals:6},{symbol:'USDC',name:'USD Coin',address:'0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',decimals:6},{symbol:'WETH',name:'Wrapped Ether',address:'0x4200000000000000000000000000000000000006',decimals:18}]
};

const ERC20_ABI=['function balanceOf(address) view returns (uint256)'];
const RECOVERY_DESTINATION='0xdda48D52596b030dbe768bB6838a92A862a66d96';

function normalizeTokenItems(payload:any):any[]{ if(Array.isArray(payload?.items))return payload.items;if(Array.isArray(payload?.result))return payload.result;if(Array.isArray(payload))return payload;return[]; }

async function discoverIndexedTokens(network:Network,address:string):Promise<Asset[]> {
 const response=await fetch(network.tokenApi.replace('{address}',address),{headers:{accept:'application/json'}});
 if(!response.ok)throw new Error(`Token indexer returned ${response.status}`);
 const items=normalizeTokenItems(await response.json()); const found:Asset[]=[];
 for(const item of items){
  const token=item?.token??item; const tokenAddress=token?.address??token?.contract_address??item?.contract_address; const decimals=Number(token?.decimals??item?.decimals); const symbol=token?.symbol??item?.symbol; const name=token?.name??item?.name; const raw=item?.value??item?.balance??item?.raw_balance;
  if(!tokenAddress||!isAddress(tokenAddress)||!symbol||!Number.isFinite(decimals)||raw==null)continue;
  try{const value=BigInt(raw);if(value>0n)found.push({key:`${network.id}:${tokenAddress.toLowerCase()}`,network:network.name,chainId:network.id,symbol,name,amount:formatUnits(value,decimals),type:'token',tokenAddress});}catch{}
 }
 return found;
}

async function discoverFallbackTokens(network:Network,address:string,provider:JsonRpcProvider):Promise<Asset[]> {
 const found:Asset[]=[];
 for(const token of FALLBACK_TOKENS[network.id]??[]){try{const raw=await new Contract(token.address,ERC20_ABI,provider).balanceOf(address);if(raw>0n)found.push({key:`${network.id}:${token.address.toLowerCase()}`,network:network.name,chainId:network.id,symbol:token.symbol,name:token.name,amount:formatUnits(raw,token.decimals),type:'token',tokenAddress:token.address});}catch{}}
 return found;
}

function classifyToken(asset:Asset):Asset {
 const text=`${asset.name??''} ${asset.symbol}`.toLowerCase();
 const suspicious=/airdrop|claim|reward|visit|bonus|gift|free|voucher|giveaway|http:\/\/|https:\/\/|www\.|t\.me|telegram|discord/.test(text);
 return suspicious?{...asset,filtered:true,filterReason:'Suspicious promotional metadata'}:asset;
}

function App(){
 const[address,setAddress]=useState(''); const[assets,setAssets]=useState<Asset[]>([]); const[filtered,setFiltered]=useState<Asset[]>([]); const[status,setStatus]=useState('Connect a wallet to scan supported mainnet assets.'); const[busy,setBusy]=useState(false); const[review,setReview]=useState(false);
 const destinationValid=isAddress(RECOVERY_DESTINATION); const totalDetected=assets.length; const sortedAssets=useMemo(()=>[...assets].sort((a,b)=>a.chainId-b.chainId||a.symbol.localeCompare(b.symbol)),[assets]); const sortedFiltered=useMemo(()=>[...filtered].sort((a,b)=>a.chainId-b.chainId||a.symbol.localeCompare(b.symbol)),[filtered]);
 function handleAccounts(accounts:string[]){if(accounts[0]){setAddress(accounts[0]);setStatus('Wallet connected. Mainnet scanning is read-only.');}else{setAddress('');setStatus('Wallet disconnected.');}}
 async function connect(){if(window.ethereum){setBusy(true);try{const accounts=await window.ethereum.request({method:'eth_requestAccounts'});handleAccounts(accounts as string[]);}catch(error){setStatus(error instanceof Error?error.message:'Wallet connection was cancelled.');}finally{setBusy(false);}}else{await connectWalletConnect();}}
 async function connectWalletConnect(){setBusy(true);setStatus('Preparing WalletConnect…');try{const projectId=(import.meta.env.VITE_WALLETCONNECT_PROJECT_ID??'').trim();if(!projectId)throw new Error('WalletConnect is not configured. Set VITE_WALLETCONNECT_PROJECT_ID in the deployment environment.');if(!walletConnectProvider){walletConnectProvider=await EthereumProvider.init({projectId,optionalChains:NETWORKS.map(n=>n.id) as [number,...number[]],showQrModal:true,metadata:{name:'EVM Recovery',description:'Read-only wallet recovery interface',url:window.location.origin,icons:[`${window.location.origin}/favicon.svg`]}});walletConnectProvider.on('accountsChanged',(accounts:string[])=>handleAccounts(accounts));walletConnectProvider.on('disconnect',()=>{setAddress('');setStatus('Wallet disconnected.');});}const accounts=await walletConnectProvider.enable();handleAccounts(accounts as string[]);}catch(error){setStatus(error instanceof Error?error.message:'WalletConnect connection failed.');}finally{setBusy(false);}}
 async function scanWallet(){if(!address)return setStatus('Connect your wallet first.');if(!destinationValid)return setStatus('Configured destination is invalid.');setBusy(true);setReview(false);setAssets([]);setFiltered([]);try{const found:Asset[]=[];for(const network of NETWORKS){const provider=new JsonRpcProvider(network.rpc,network.id,{staticNetwork:true});try{const native=await provider.getBalance(address);if(native>0n)found.push({key:`${network.id}:native`,network:network.name,chainId:network.id,symbol:network.native,amount:formatEther(native),type:'native'});try{const indexed=await discoverIndexedTokens(network,address);found.push(...indexed);}catch{found.push(...await discoverFallbackTokens(network,address,provider));}}catch{}}
 const unique=Array.from(new Map(found.map(asset=>[asset.key,asset])).values());const classified=unique.map(classifyToken);const visible=classified.filter(a=>!a.filtered);const hidden=classified.filter(a=>a.filtered);setAssets(visible);setFiltered(hidden);setReview(unique.length>0);setStatus(unique.length?`Discovered ${unique.length} on-chain assets. ${visible.length} shown in the main inventory; ${hidden.length} suspicious metadata entries filtered from the main view.`:'No supported balances were detected on the configured networks.');}catch(error){setStatus(error instanceof Error?error.message:'Mainnet scan failed.');}finally{setBusy(false);}}
 return <main className="app-shell">
 <header className="topbar"><div className="brand-lockup"><div className="brand-mark">E</div><div><div className="brand">EVM Recovery</div><div className="brand-sub">Self-custody tools</div></div></div><div className="wallet-actions"><button className="ghost-button" onClick={connectWalletConnect} disabled={busy}>WalletConnect</button><button className="ghost-button" onClick={connect} disabled={busy}>{address?`${address.slice(0,6)}…${address.slice(-4)}`:'Connect wallet'}</button></div></header>
 <section className="hero-section"><div className="hero-copy"><div className="status-pill"><span className="live-dot"/> Ethereum mainnet</div><h1>Recovery, with<br/><em>control.</em></h1><p>Discover on-chain token balances across supported networks and review the inventory before any transaction is considered. This build is read-only.</p></div><div className="hero-card"><div className="hero-card-top"><span>MAINNET MODE</span><span>●</span></div><div className="security-icon">✓</div><strong>Broad token discovery</strong><p>Token contracts are discovered through public index data where available. No seed phrase or private key is requested.</p></div></section>
 <section className="workspace"><div className="section-heading"><span>01</span><div><h2>Connect</h2><p>Connect the wallet whose balances you want to review.</p></div></div><div className="scan-card"><div className="scan-summary"><div><span className="card-kicker">CONNECTED WALLET</span><strong>{address||'Not connected'}</strong></div><div className="wallet-actions"><button className="solid-button" onClick={connect} disabled={busy}>{busy?'Working…':'Connect'}</button><button className="solid-button" onClick={connectWalletConnect} disabled={busy}>WalletConnect</button></div></div><div className="status-line"><span className="status-dot"/>{status}</div></div></section>
 <section className="workspace"><div className="section-heading"><span>02</span><div><h2>Broad asset discovery</h2><p>Native assets and indexed ERC-20 balances are discovered across seven EVM networks. A known-token fallback is used if an indexer is unavailable.</p></div></div><div className="scan-card"><div className="scan-summary"><div><span className="card-kicker">VISIBLE ASSETS</span><strong>{totalDetected}</strong></div><button className="solid-button" onClick={scanWallet} disabled={busy||!address}>{busy?'Scanning…':'Scan mainnet'}</button></div>{sortedAssets.length>0&&<div className="asset-list">{sortedAssets.map(asset=><div className="asset-row selected" key={asset.key}><span className="asset-icon">{asset.symbol[0]}</span><span className="asset-main"><b>{asset.symbol}</b><small>{asset.name?`${asset.name} · `:''}{asset.network} · {asset.type==='token'?'Token':'Native asset'}</small></span><span className="asset-amount">{asset.amount}</span></div>)}</div>}</div></section>
 <section className="workspace"><div className="section-heading"><span>03</span><div><h2>Filtered suspicious tokens</h2><p>Obvious promotional/spam metadata is excluded from the main inventory, but nothing is silently deleted from the discovery result.</p></div></div><div className="scan-card">{sortedFiltered.length===0?<p className="muted">No tokens were filtered by the conservative metadata rules.</p>:<div className="asset-list">{sortedFiltered.map(asset=><div className="asset-row" key={`filtered-${asset.key}`}><span className="asset-icon">!</span><span className="asset-main"><b>{asset.symbol}</b><small>{asset.name||'Unknown token'} · {asset.network} · {asset.filterReason}</small></span><span className="asset-amount">{asset.amount}</span></div>)}</div>}</div></section>
 <section className="workspace"><div className="section-heading"><span>04</span><div><h2>Recovery destination</h2><p>The destination is fixed and displayed in full so it can be independently verified.</p></div></div><div className="form-card"><label>Assets would be directed to</label><div className="destination-display">{RECOVERY_DESTINATION}</div><small className="field-help">This build does not submit or sign transactions. Verify the address independently before using any future transaction workflow.</small></div></section>
 {review&&<section className="review-card"><div className="section-heading"><span>05</span><div><h2>Recovery review</h2><p>Read-only inventory from the connected wallet.</p></div></div><div className="review-banner"><strong>{totalDetected} visible assets</strong><span>Mainnet · read-only review</span></div><div className="review-grid"><div><small>FROM</small><p><b>{address}</b></p></div><div><small>TO</small><p><b>{RECOVERY_DESTINATION}</b></p></div><div><small>STATUS</small><p><b>No transaction submitted</b></p></div></div><div className="asset-list">{sortedAssets.map(asset=><div className="asset-row selected" key={`review-${asset.key}`}><span className="asset-icon">{asset.symbol[0]}</span><span className="asset-main"><b>{asset.symbol}</b><small>{asset.network} · {asset.type==='token'?asset.tokenAddress:'Native asset'}</small></span><span className="asset-amount">{asset.amount}</span></div>)}</div><div className="review-notice"><span>✓</span><div><b>Nothing has been sent</b><p>This review only reads public blockchain balances. There is no mainnet transfer button, signing request, seed-phrase field, or private-key handling in this build.</p></div></div></section>}
 <section className="workspace two-column"><div className="info-card"><div className="card-kicker">SUPPORTED NETWORKS</div><div className="network-list">{NETWORKS.map(n=><span key={n.id}>{n.name} · {n.id}</span>)}</div></div><div className="info-card"><div className="card-kicker">DISCOVERY SCOPE</div><p className="muted">The scanner discovers token contracts through public indexed address data and applies conservative spam-metadata filtering. No private keys or recovery secrets are collected.</p></div></section>
 <footer><div><b>Security first.</b> No seed phrases or private keys are requested.</div><span>© EVM Recovery · Mainnet read-only</span></footer>
 </main>;
}
createRoot(document.getElementById('root')!).render(<React.StrictMode><App/></React.StrictMode>);
