import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { Environment, ParaProvider } from '@getpara/react-sdk';
import '@getpara/react-sdk/styles.css';
import { useSbcPara } from '@stablecoin.xyz/react';
import { radiusTestnet, TestSBC_CONTRACT_ADDRESS } from '@stablecoin.xyz/core';
import { useAccount, useWallet, useSignMessage } from '@getpara/react-sdk';
import { baseSepolia, base, type Chain } from 'viem/chains';
import { createPublicClient, http, getAddress, parseUnits, encodeFunctionData, erc20Abi } from 'viem';
import { hexToBytes } from 'viem/utils';
import { useMemo, useState, useEffect } from 'react';
import { ConnectButton } from './components/ConnectButton';
import { buildPermitTypedData, hashPermitTypedData, hex32ToBase64, normalizeSignatureToRSV, deriveVForRS } from './utils/permit';
import { usePara } from './hooks/usePara';

const queryClient = new QueryClient();

const getChain = () => {
  const chainEnv = import.meta.env.VITE_CHAIN;
  if (chainEnv === 'base') return base;
  if (chainEnv === 'radiusTestnet') return radiusTestnet;
  return baseSepolia;
};

const chain = getChain();
const rpcUrl = import.meta.env.VITE_RPC_URL;

// Radius testnet uses TestSBC (a test token for development)
const TEST_SBC_DECIMALS = 6;

const SBC_TOKEN_ADDRESS = (chain: Chain) => {
  if (chain.id === baseSepolia.id) return '0xf9FB20B8E097904f0aB7d12e9DbeE88f2dcd0F16';
  if (chain.id === base.id) return '0xfdcC3dd6671eaB0709A4C0f3F53De9a333d80798';
  if (chain.id === radiusTestnet.id) return TestSBC_CONTRACT_ADDRESS;
  throw new Error('Unsupported chain');
};

const SBC_DECIMALS = (chain: Chain) => {
  if (chain.id === baseSepolia.id) return 6;
  if (chain.id === base.id) return 18;
  if (chain.id === radiusTestnet.id) return TEST_SBC_DECIMALS;
  throw new Error('Unsupported chain');
};

const getTokenSymbol = (chain: Chain) => {
  if (chain.id === radiusTestnet.id) return 'TestSBC';
  return 'SBC';
};

const publicClient = createPublicClient({ chain, transport: http(rpcUrl) });

// ERC20 + EIP-2612 nonces helper ABI
const erc20PermitAbi = [
  ...erc20Abi,
  {
    "inputs": [
      { "internalType": "address", "name": "owner", "type": "address" }
    ],
    "name": "nonces",
    "outputs": [
      { "internalType": "uint256", "name": "", "type": "uint256" }
    ],
    "stateMutability": "view",
    "type": "function"
  }
] as const;

// Minimal permit ABI for encoding the permit call
const permitAbi = [
  {
    "inputs": [
      { "internalType": "address", "name": "owner", "type": "address" },
      { "internalType": "address", "name": "spender", "type": "address" },
      { "internalType": "uint256", "name": "value", "type": "uint256" },
      { "internalType": "uint256", "name": "deadline", "type": "uint256" },
      { "internalType": "uint8", "name": "v", "type": "uint8" },
      { "internalType": "bytes32", "name": "r", "type": "bytes32" },
      { "internalType": "bytes32", "name": "s", "type": "bytes32" }
    ],
    "name": "permit",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  }
] as const;

const chainExplorer = (c: Chain) => {
  if (c.id === baseSepolia.id) return 'https://sepolia.basescan.org';
  if (c.id === base.id) return 'https://basescan.org';
  if (c.id === radiusTestnet.id) return 'https://testnet.radiustech.xyz/testnet/explorer';
  return '';
};

function SmartAccountInfo({ account, refreshAccount, isLoadingAccount, accountError }: any) {
  const [sbcBalance, setSbcBalance] = useState<string | null>(null);
  useEffect(() => {
    if (!account?.address) return;
    (async () => {
      try {
        const bal = await publicClient.readContract({
          address: SBC_TOKEN_ADDRESS(chain) as `0x${string}`,
          abi: erc20Abi,
          functionName: 'balanceOf',
          args: [account.address as `0x${string}`],
        });
        setSbcBalance((bal as bigint).toString());
      } catch (error) {
        console.error('Failed to fetch smart account SBC balance:', error);
        setSbcBalance('0');
      }
    })();
  }, [account?.address]);

  if (!account) return null;
  const fmtEth = (v: string | null) => v ? (Number(v) / 1e18).toFixed(6) : '0.000000';
  const fmtSbc = (v: string | null) => v ? (Number(v) / Math.pow(10, SBC_DECIMALS(chain))).toFixed(2) : '0.00';

  return (
    <div className="mb-6 p-4 bg-purple-50 border border-purple-200 rounded-lg">
      <div className="flex justify-between items-center mb-2">
        <h3 className="font-semibold text-purple-800">🔐 Smart Account Status</h3>
        <button onClick={refreshAccount} disabled={isLoadingAccount} className="text-xs bg-purple-600 text-white px-3 py-1 rounded hover:bg-purple-700 disabled:opacity-50">{isLoadingAccount ? '🔄 Refreshing...' : '🔄 Refresh'}</button>
      </div>
      <div className="space-y-2 text-sm">
        <div className="flex justify-between"><span className="text-purple-700">Smart Account Address:</span><span className="font-mono text-xs text-purple-600 break-all">{account.address}</span></div>
        <div className="flex justify-between"><span className="text-purple-700">Deployed:</span><span className="text-purple-600">{account.isDeployed ? '✅ Yes' : '⏳ On first transaction'}</span></div>
        <div className="flex justify-between"><span className="text-purple-700">Nonce:</span><span className="text-purple-600">{account.nonce}</span></div>
        <div className="flex justify-between"><span className="text-purple-700">ETH Balance:</span><span className="text-purple-600 font-mono text-xs">{fmtEth(account.balance)} ETH</span></div>
        <div className="flex justify-between"><span className="text-purple-700">{getTokenSymbol(chain)} Balance:</span><span className="text-purple-600 font-mono text-xs">{fmtSbc(sbcBalance)} {getTokenSymbol(chain)}</span></div>
      </div>
      {accountError && <p className="mt-2 text-xs text-red-600">{String(accountError)}</p>}
    </div>
  );
}

function TransactionForm({ account, sbcAppKit }: { account: any; sbcAppKit: any }) {
  const paraAccount = useAccount();
  const { data: wallet } = useWallet();
  const signMessage = useSignMessage();
  const [recipient, setRecipient] = useState('');
  const [amount, setAmount] = useState('1');
  const [status, setStatus] = useState<'idle'|'loading'|'success'|'error'>('idle');
  const [error, setError] = useState<string | null>(null);
  const [result, setResult] = useState<any>(null);
  const isValid = recipient && /^0x[a-fA-F0-9]{40}$/.test(recipient) && parseFloat(amount) > 0;

  const sendTx = async () => {
    if (!isValid || !account || !sbcAppKit || !wallet?.id) return;
    try {
      setStatus('loading'); setError(null); setResult(null);
      // Determine Para owner address (external or embedded)
      const isExternalWallet = paraAccount.isConnected && paraAccount.external?.evm?.address;
      const isEmbeddedWallet = paraAccount.isConnected && paraAccount.embedded?.wallets && paraAccount.embedded.wallets.length > 0;
      const walletAddress = isExternalWallet
        ? paraAccount.external?.evm?.address
        : isEmbeddedWallet
          ? paraAccount.embedded.wallets?.[0]?.address
          : null;
      if (!walletAddress) throw new Error('No Para wallet address');

      const owner = getAddress(walletAddress as `0x${string}`);
      const spender = getAddress(account.address);
      const value = parseUnits(amount, SBC_DECIMALS(chain));
      const deadline = Math.floor(Date.now() / 1000) + 60 * 30;
      const [nonce, tokenName] = await Promise.all([
        publicClient.readContract({ address: SBC_TOKEN_ADDRESS(chain) as `0x${string}`, abi: erc20PermitAbi, functionName: 'nonces', args: [owner] }),
        publicClient.readContract({ address: SBC_TOKEN_ADDRESS(chain) as `0x${string}`, abi: erc20Abi, functionName: 'name' })
      ]);

      const typed = buildPermitTypedData({
        tokenName: tokenName as string,
        chainId: chain.id,
        tokenAddress: SBC_TOKEN_ADDRESS(chain) as `0x${string}`,
        owner,
        spender,
        value,
        nonce: nonce as bigint,
        deadline: BigInt(deadline)
      });

      const digest = hashPermitTypedData(typed);
      const digestBase64 = hex32ToBase64(digest);
      const sigRes = await signMessage.signMessageAsync({ walletId: wallet.id, messageBase64: digestBase64 });
      const paraSig = (sigRes as any)?.signatureBase64 || (sigRes as any)?.signature || (typeof sigRes === 'string' ? sigRes : '');
      let { r, s, v } = normalizeSignatureToRSV(paraSig);
      if (!v || v === 0) {
        v = await deriveVForRS({ digest, r, s, owner });
      }

      const permitData = encodeFunctionData({
        abi: permitAbi,
        functionName: 'permit',
        args: [owner, spender, value, BigInt(deadline), v, r, s]
      });
      const transferFromData = encodeFunctionData({
        abi: erc20Abi,
        functionName: 'transferFrom',
        args: [owner, recipient as `0x${string}`, value]
      });
      const res = await sbcAppKit.sendUserOperation({
        calls: [
          { to: SBC_TOKEN_ADDRESS(chain) as `0x${string}`, data: permitData },
          { to: SBC_TOKEN_ADDRESS(chain) as `0x${string}`, data: transferFromData }
        ]
      });
      setResult(res);
      setStatus('success');
    } catch (e: any) {
      setError(e?.message || 'Transaction failed');
      setStatus('error');
    }
  };

  if (!account) return null;

  return (
    <div className="p-4 bg-white border border-gray-200 rounded-lg shadow-sm">
      <h3 className="font-semibold text-gray-800 mb-4">💸 Send {getTokenSymbol(chain)} Tokens</h3>
      <div className="space-y-4">
        <div>
          <label className="block text-sm font-medium text-gray-700 mb-2">Recipient Address</label>
          <input type="text" value={recipient} onChange={(e) => setRecipient(e.target.value)} placeholder="0x..." className="w-full px-3 py-2 text-xs font-mono border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 border-gray-300" />
        </div>
        <div>
          <label className="block text-sm font-medium text-gray-700 mb-2">Amount ({getTokenSymbol(chain)})</label>
          <input type="number" value={amount} onChange={(e) => setAmount(e.target.value)} placeholder="1.0" step="0.000001" min="0" className="w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 border-gray-300" />
        </div>
        <div className="p-3 bg-gray-50 rounded">
          <div className="flex justify-between text-sm"><span>Amount:</span><span className="font-medium">{amount} {getTokenSymbol(chain)}</span></div>
          <div className="flex justify-between text-xs text-gray-600"><span>Gas fees:</span><span>Covered by SBC Paymaster ✨</span></div>
          <div className="flex justify-between text-xs text-gray-600"><span>Signing:</span><span>Your wallet will prompt to sign 🖊️</span></div>
        </div>
        <button onClick={sendTx} disabled={!isValid || status==='loading' || !account} className="w-full bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed">
          {status==='loading' ? 'Waiting for signature...' : `Send ${amount} ${getTokenSymbol(chain)}`}
        </button>
        {status==='success' && result && (
          <div className="p-3 bg-green-50 border border-green-200 rounded">
            <p className="text-sm text-green-800 font-medium">✅ Transaction Submitted</p>
            <p className="text-xs text-green-600 font-mono break-all mt-1">
              <a href={`${chainExplorer(chain)}/tx/${result.transactionHash}`} target="_blank" rel="noopener noreferrer" className="text-blue-600 hover:underline">View transaction: {result.transactionHash}</a>
            </p>
          </div>
        )}
        {status==='error' && error && (
          <div className="p-3 bg-red-50 border border-red-200 rounded">
            <p className="text-sm text-red-800 font-medium">❌ Transaction Failed</p>
            <p className="text-xs text-red-600 mt-1">{error}</p>
          </div>
        )}
      </div>
    </div>
  );
}

function ParaApp() {
  const paraAccount = useAccount();
  // Get Para viem clients (walletClient/account) to pass into SBC hook
  const { publicClient: paraPublicClient, walletClient: paraWalletClient, account: paraViemAccount } = usePara();
  const { data: wallet } = useWallet();
  const signMsg = useSignMessage();

  // Wrap walletClient.signMessage to route through Para signMessage (base64) and return 65-byte hex
  const wrappedWalletClient = useMemo(() => {
    if (!paraWalletClient || !paraViemAccount || !wallet?.id) return null;
    const base = paraWalletClient as any;
    const account = {
      ...paraViemAccount,
      async signMessage({ message }: any) {
        const toBytes = (m: any): Uint8Array => {
          if (!m) throw new Error('signMessage: missing message');
          if (typeof m === 'string') {
            if (m.startsWith('0x')) return hexToBytes(m as `0x${string}`);
            return new TextEncoder().encode(m);
          }
          const raw = m.raw ?? m.bytes ?? m.data ?? m;
          if (typeof raw === 'string') {
            if (raw.startsWith('0x')) return hexToBytes(raw as `0x${string}`);
            return new TextEncoder().encode(raw);
          }
          if (raw instanceof Uint8Array) return raw;
          if (raw instanceof ArrayBuffer) return new Uint8Array(raw);
          if (Array.isArray(raw)) return new Uint8Array(raw);
          throw new Error('signMessage: unsupported message format');
        };
        const rawBytes = toBytes(message);
        const b64 = btoa(String.fromCharCode(...rawBytes));
        const res = await signMsg.signMessageAsync({ walletId: wallet.id, messageBase64: b64 });
        const sig = (res as any)?.signatureBase64 || (res as any)?.signature || (typeof res === 'string' ? res : '');
        const { r, s, v } = normalizeSignatureToRSV(sig);
        const vHex = (v < 27 ? v + 27 : v).toString(16).padStart(2, '0');
        return (r + s.slice(2) + vHex) as `0x${string}`;
      },
    };
    return { ...base, account, async signMessage(args: any) { return account.signMessage(args); } };
  }, [paraWalletClient, paraViemAccount, wallet?.id]);

  const paraViemClients = useMemo(() => ({
    publicClient: paraPublicClient,
    walletClient: wrappedWalletClient || paraWalletClient,
    account: paraViemAccount,
  }), [paraPublicClient, wrappedWalletClient, paraWalletClient, paraViemAccount]);

  const { sbcAppKit, isInitialized, error, account, isLoadingAccount, accountError, refreshAccount } = useSbcPara({
    apiKey: import.meta.env.VITE_SBC_API_KEY,
    chain,
    paraAccount,
    rpcUrl,
    debug: true,
    paraViemClients,
  });

  const isConnected = paraAccount.isConnected && (paraAccount.external?.evm?.address || paraAccount.embedded?.wallets?.length);

  return (
    <>
      <ConnectButton />
      {isConnected && isInitialized && (
        <>
          <SmartAccountInfo account={account} refreshAccount={refreshAccount} isLoadingAccount={isLoadingAccount} accountError={accountError} />
          <TransactionForm account={account} sbcAppKit={sbcAppKit} />
        </>
      )}
      {error && <div className="error">{error.message}</div>}
    </>
  );
}

export default function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <ParaProvider
        paraClientConfig={{ apiKey: import.meta.env.VITE_PARA_API_KEY, env: Environment.PROD }}
        config={{ appName: '{{projectName}}' }}
        externalWalletConfig={{ wallets: ['METAMASK', 'COINBASE'] }}
      >
        <div className="min-h-screen bg-gray-50 py-8">
          <div className="max-w-2xl mx-auto px-4">
            <div className="text-center mb-8">
              <h1 className="text-3xl font-bold text-gray-900 mb-2 flex items-center justify-center gap-3">
                <img src="/sbc-logo.png" alt="SBC Logo" width={36} height={36} />
                SBC (Para) Integration
              </h1>
              <p className="text-gray-600">Gasless transactions with Para Wallet</p>
            </div>

            <ParaApp />

            <div className="mt-8 text-center text-xs text-gray-500">
              <p>
                Powered by{' '}
                <a href="https://github.com/stablecoinxyz/app-kit" target="_blank" rel="noopener noreferrer" className="text-blue-600 hover:underline">SBC AppKit</a>
                {' '}• Para SDK integration
              </p>
            </div>
          </div>
        </div>
      </ParaProvider>
    </QueryClientProvider>
  );
}


