import { useState, useMemo } from 'react'
import { useAccount, useChainId } from 'wagmi'
import { useReadContract, useWriteContract, useWaitForTransactionReceipt } from 'wagmi'
import { CONTRACT_ADDRESSES } from '../../config/contracts'
import { PAYMENT_CHANNEL_MANAGER_ABI } from '../../abis/PaymentChannelManager'
import toast from 'react-hot-toast'
import { chain138, chain2138Testnet } from '../../config/networks'

const CHANNEL_STATUS = ['None', 'Open', 'Dispute', 'Closed'] as const

function getManagerAddress(chainId: number): `0x${string}` | undefined {
  if (chainId === 1) return CONTRACT_ADDRESSES.mainnet.PAYMENT_CHANNEL_MANAGER as `0x${string}` | undefined
  if (chainId === 138) return CONTRACT_ADDRESSES.chain138.PAYMENT_CHANNEL_MANAGER as `0x${string}` | undefined
  if (chainId === 2138) return CONTRACT_ADDRESSES.chain2138.PAYMENT_CHANNEL_MANAGER as `0x${string}` | undefined
  return undefined
}

export default function PaymentChannels() {
  const { address } = useAccount()
  const chainId = useChainId()
  const managerAddress = getManagerAddress(chainId)

  const [counterparty, setCounterparty] = useState('')
  const [openAmount, setOpenAmount] = useState('')
  const [fundChannelId, setFundChannelId] = useState('')
  const [fundAmount, setFundAmount] = useState('')
  const [finalizeChannelId, setFinalizeChannelId] = useState('')
  const [showAdvancedClose, setShowAdvancedClose] = useState(false)
  const [closeChannelId, setCloseChannelId] = useState('')
  const [closeNonce, setCloseNonce] = useState('')
  const [closeBalanceA, setCloseBalanceA] = useState('')
  const [closeBalanceB, setCloseBalanceB] = useState('')
  const [closeVA, setCloseVA] = useState('')
  const [closeRA, setCloseRA] = useState('')
  const [closeSA, setCloseSA] = useState('')
  const [closeVB, setCloseVB] = useState('')
  const [closeRB, setCloseRB] = useState('')
  const [closeSB, setCloseSB] = useState('')

  const { data: channelCount = 0n } = useReadContract({
    address: managerAddress,
    abi: PAYMENT_CHANNEL_MANAGER_ABI,
    functionName: 'getChannelCount',
  })

  const { data: paused } = useReadContract({
    address: managerAddress,
    abi: PAYMENT_CHANNEL_MANAGER_ABI,
    functionName: 'paused',
  })

  const { writeContract, data: hash, isPending } = useWriteContract()
  const { isLoading: isConfirming } = useWaitForTransactionReceipt({ hash })

  const channelIds = useMemo(() => {
    const n = Number(channelCount)
    return Array.from({ length: n }, (_, i) => i)
  }, [channelCount])

  const openChannel = () => {
    if (!managerAddress || !counterparty || !openAmount) {
      toast.error('Enter counterparty and amount')
      return
    }
    const wei = BigInt(openAmount) * 10n ** 18n
    if (wei <= 0n) {
      toast.error('Amount must be > 0')
      return
    }
    writeContract(
      {
        address: managerAddress,
        abi: PAYMENT_CHANNEL_MANAGER_ABI,
        functionName: 'openChannel',
        args: [counterparty as `0x${string}`],
        value: wei,
      },
      {
        onSuccess: () => {
          toast.success('Open channel tx submitted')
          setCounterparty('')
          setOpenAmount('')
        },
        onError: (e: unknown) => toast.error((e as Error).message),
      }
    )
  }

  const fundChannel = () => {
    if (!managerAddress || fundChannelId === '' || !fundAmount) {
      toast.error('Enter channel ID and amount')
      return
    }
    const wei = BigInt(fundAmount) * 10n ** 18n
    if (wei <= 0n) {
      toast.error('Amount must be > 0')
      return
    }
    writeContract(
      {
        address: managerAddress,
        abi: PAYMENT_CHANNEL_MANAGER_ABI,
        functionName: 'fundChannel',
        args: [BigInt(fundChannelId)],
        value: wei,
      },
      {
        onSuccess: () => {
          toast.success('Fund channel tx submitted')
          setFundChannelId('')
          setFundAmount('')
        },
        onError: (e: unknown) => toast.error((e as Error).message),
      }
    )
  }

  const doFinalizeClose = () => {
    if (!managerAddress || finalizeChannelId === '') {
      toast.error('Enter channel ID')
      return
    }
    writeContract(
      {
        address: managerAddress,
        abi: PAYMENT_CHANNEL_MANAGER_ABI,
        functionName: 'finalizeClose',
        args: [BigInt(finalizeChannelId)],
      },
      {
        onSuccess: () => {
          toast.success('Finalize close tx submitted')
          setFinalizeChannelId('')
        },
        onError: (e: unknown) => toast.error((e as Error).message),
      }
    )
  }

  const doCooperativeClose = () => {
    if (!managerAddress || !closeChannelId || !closeNonce || !closeBalanceA || !closeBalanceB) {
      toast.error('Fill channel ID, nonce, balanceA, balanceB')
      return
    }
    const balA = BigInt(closeBalanceA) * 10n ** 18n
    const balB = BigInt(closeBalanceB) * 10n ** 18n
    const vA = parseInt(closeVA || '0', 10)
    const vB = parseInt(closeVB || '0', 10)
    if (!closeRA || !closeSA || !closeRB || !closeSB || closeRA.length !== 66 || closeSA.length !== 66 || closeRB.length !== 66 || closeSB.length !== 66) {
      toast.error('Enter v (0-255) and 0x-prefixed 32-byte hex for r and s (both parties)')
      return
    }
    writeContract(
      {
        address: managerAddress,
        abi: PAYMENT_CHANNEL_MANAGER_ABI,
        functionName: 'closeChannelCooperative',
        args: [BigInt(closeChannelId), BigInt(closeNonce), balA, balB, vA, closeRA as `0x${string}`, closeSA as `0x${string}`, vB, closeRB as `0x${string}`, closeSB as `0x${string}`],
      },
      {
        onSuccess: () => { toast.success('Cooperative close submitted'); setCloseChannelId(''); setCloseNonce(''); setCloseBalanceA(''); setCloseBalanceB(''); setCloseVA(''); setCloseRA(''); setCloseSA(''); setCloseVB(''); setCloseRB(''); setCloseSB('') },
        onError: (e: unknown) => toast.error((e as Error).message),
      }
    )
  }

  const doSubmitClose = () => {
    if (!managerAddress || !closeChannelId || !closeNonce || !closeBalanceA || !closeBalanceB) {
      toast.error('Fill channel ID, nonce, balanceA, balanceB')
      return
    }
    const balA = BigInt(closeBalanceA) * 10n ** 18n
    const balB = BigInt(closeBalanceB) * 10n ** 18n
    const vA = parseInt(closeVA || '0', 10)
    const vB = parseInt(closeVB || '0', 10)
    if (!closeRA || !closeSA || !closeRB || !closeSB) {
      toast.error('Enter v (0-255) and 0x-prefixed hex for r, s (both parties)')
      return
    }
    writeContract(
      {
        address: managerAddress,
        abi: PAYMENT_CHANNEL_MANAGER_ABI,
        functionName: 'submitClose',
        args: [BigInt(closeChannelId), BigInt(closeNonce), balA, balB, vA, closeRA as `0x${string}`, closeSA as `0x${string}`, vB, closeRB as `0x${string}`, closeSB as `0x${string}`],
      },
      {
        onSuccess: () => { toast.success('Submit close tx submitted'); setCloseChannelId(''); setCloseNonce(''); setCloseBalanceA(''); setCloseBalanceB('') },
        onError: (e: unknown) => toast.error((e as Error).message),
      }
    )
  }

  if (managerAddress == null) {
    return (
      <div className="rounded-xl bg-white/5 p-6 text-white/80">
        <p>
          Payment channel manager not deployed on this chain. Set `PAYMENT_CHANNEL_MANAGER`
          in config for Ethereum Mainnet, {chain138.name}, or {chain2138Testnet.name}.
        </p>
      </div>
    )
  }

  return (
    <div className="space-y-8">
      <div className="rounded-xl bg-white/5 p-6">
        <h2 className="text-xl font-semibold text-white mb-4">Payment Channels</h2>
        {paused && (
          <div className="mb-4 p-3 rounded-lg bg-amber-500/20 text-amber-200 text-sm">Contract is paused. Only close/finalize allowed.</div>
        )}
        <p className="text-white/70 text-sm mb-4">
          Open a channel with a counterparty, fund it (optional second side), then close cooperatively or via dispute window. On {chain138.name}, channel transactions are mirrored to Mainnet.
        </p>
        <div className="text-white/60 text-xs mb-4 space-y-1">
          <p>Payment channels (above) = state channels for payments. For cross-chain or routed payments, general state channels, or channel networks:</p>
          <ul className="list-disc list-inside ml-2 space-y-0.5">
            <li>
              <a href="https://docs.connext.network/" target="_blank" rel="noopener noreferrer" className="text-blue-400 hover:underline">Connext</a>
              {' '}(cross-chain, xcalls, routed payments)
            </li>
            <li>
              <a href="https://docs.raiden.network/" target="_blank" rel="noopener noreferrer" className="text-blue-400 hover:underline">Raiden</a>
              {' '}(payment channel network, path finding)
            </li>
            <li>
              <a href="https://docs.statechannels.org/" target="_blank" rel="noopener noreferrer" className="text-blue-400 hover:underline">Statechannels.org</a>
              {' '}(general state channel framework)
            </li>
          </ul>
          <p>See docs/channels/EXTERNAL_PROTOCOL_INTEGRATION.md and docs/channels/STATE_CHANNELS_VS_PAYMENT_CHANNELS.md.</p>
        </div>

        <div className="grid gap-6 md:grid-cols-2">
          <div className="space-y-2">
            <label className="block text-sm text-white/80">Open channel (you deposit)</label>
            <input
              type="text"
              placeholder="Counterparty address 0x..."
              value={counterparty}
              onChange={(e) => setCounterparty(e.target.value)}
              className="w-full rounded-lg bg-black/30 border border-white/20 px-4 py-2 text-white placeholder-white/40"
            />
            <input
              type="text"
              placeholder="Amount (ETH)"
              value={openAmount}
              onChange={(e) => setOpenAmount(e.target.value)}
              className="w-full rounded-lg bg-black/30 border border-white/20 px-4 py-2 text-white placeholder-white/40"
            />
            <button
              onClick={openChannel}
              disabled={isPending || isConfirming || !!paused}
              className="rounded-lg bg-blue-600 px-4 py-2 text-white hover:bg-blue-700 disabled:opacity-50"
            >
              {isPending || isConfirming ? 'Confirming...' : 'Open channel'}
            </button>
          </div>

          <div className="space-y-2">
            <label className="block text-sm text-white/80">Fund channel (participant B)</label>
            <input
              type="text"
              placeholder="Channel ID"
              value={fundChannelId}
              onChange={(e) => setFundChannelId(e.target.value)}
              className="w-full rounded-lg bg-black/30 border border-white/20 px-4 py-2 text-white placeholder-white/40"
            />
            <input
              type="text"
              placeholder="Amount (ETH)"
              value={fundAmount}
              onChange={(e) => setFundAmount(e.target.value)}
              className="w-full rounded-lg bg-black/30 border border-white/20 px-4 py-2 text-white placeholder-white/40"
            />
            <button
              onClick={fundChannel}
              disabled={isPending || isConfirming || !!paused}
              className="rounded-lg bg-blue-600 px-4 py-2 text-white hover:bg-blue-700 disabled:opacity-50"
            >
              {isPending || isConfirming ? 'Confirming...' : 'Fund channel'}
            </button>
          </div>
        </div>

        <div className="mt-6 space-y-2">
          <label className="block text-sm text-white/80">Finalize close (after challenge window)</label>
          <input
            type="text"
            placeholder="Channel ID"
            value={finalizeChannelId}
            onChange={(e) => setFinalizeChannelId(e.target.value)}
            className="w-full max-w-xs rounded-lg bg-black/30 border border-white/20 px-4 py-2 text-white placeholder-white/40"
          />
          <button
            onClick={doFinalizeClose}
            disabled={isPending || isConfirming}
            className="rounded-lg bg-emerald-600 px-4 py-2 text-white hover:bg-emerald-700 disabled:opacity-50"
          >
            {isPending || isConfirming ? 'Confirming...' : 'Finalize close'}
          </button>
        </div>

        <div className="mt-6 border-t border-white/20 pt-4">
          <button type="button" onClick={() => setShowAdvancedClose((x) => !x)} className="text-sm text-blue-400 hover:underline">
            {showAdvancedClose ? 'Hide' : 'Show'} cooperative close / submit close
          </button>
          {showAdvancedClose && (
            <div className="mt-3 p-4 rounded-lg bg-black/20 space-y-3 text-sm">
              <p className="text-white/70">Both parties must sign the state off-chain: hash = keccak256(channelId, nonce, balanceA, balanceB); then sign with wallet (e.g. personal_sign). Use the same nonce and balances below. Enter v (27 or 28), r and s as 0x-prefixed 32-byte hex.</p>
              <input type="text" placeholder="Channel ID" value={closeChannelId} onChange={(e) => setCloseChannelId(e.target.value)} className="w-full rounded bg-black/30 border border-white/20 px-3 py-2 text-white" />
              <input type="text" placeholder="Nonce" value={closeNonce} onChange={(e) => setCloseNonce(e.target.value)} className="w-full rounded bg-black/30 border border-white/20 px-3 py-2 text-white" />
              <input type="text" placeholder="Balance A (ETH)" value={closeBalanceA} onChange={(e) => setCloseBalanceA(e.target.value)} className="w-full rounded bg-black/30 border border-white/20 px-3 py-2 text-white" />
              <input type="text" placeholder="Balance B (ETH)" value={closeBalanceB} onChange={(e) => setCloseBalanceB(e.target.value)} className="w-full rounded bg-black/30 border border-white/20 px-3 py-2 text-white" />
              <div className="grid grid-cols-2 gap-2">
                <span className="text-white/60">Party A sig: v</span>
                <input type="text" placeholder="27 or 28" value={closeVA} onChange={(e) => setCloseVA(e.target.value)} className="rounded bg-black/30 border border-white/20 px-2 py-1 text-white" />
                <span className="text-white/60">r (0x...)</span>
                <input type="text" placeholder="0x..." value={closeRA} onChange={(e) => setCloseRA(e.target.value)} className="rounded bg-black/30 border border-white/20 px-2 py-1 text-white font-mono" />
                <span className="text-white/60">s (0x...)</span>
                <input type="text" placeholder="0x..." value={closeSA} onChange={(e) => setCloseSA(e.target.value)} className="rounded bg-black/30 border border-white/20 px-2 py-1 text-white font-mono" />
                <span className="text-white/60">Party B sig: v</span>
                <input type="text" placeholder="27 or 28" value={closeVB} onChange={(e) => setCloseVB(e.target.value)} className="rounded bg-black/30 border border-white/20 px-2 py-1 text-white" />
                <span className="text-white/60">r (0x...)</span>
                <input type="text" placeholder="0x..." value={closeRB} onChange={(e) => setCloseRB(e.target.value)} className="rounded bg-black/30 border border-white/20 px-2 py-1 text-white font-mono" />
                <span className="text-white/60">s (0x...)</span>
                <input type="text" placeholder="0x..." value={closeSB} onChange={(e) => setCloseSB(e.target.value)} className="rounded bg-black/30 border border-white/20 px-2 py-1 text-white font-mono" />
              </div>
              <div className="flex gap-2">
                <button onClick={doCooperativeClose} disabled={isPending || isConfirming} className="rounded bg-blue-600 px-3 py-1.5 text-white text-sm disabled:opacity-50">Close cooperatively</button>
                <button onClick={doSubmitClose} disabled={isPending || isConfirming} className="rounded bg-amber-600 px-3 py-1.5 text-white text-sm disabled:opacity-50">Submit close (start dispute)</button>
              </div>
            </div>
          )}
        </div>
      </div>

      <ChannelList managerAddress={managerAddress} channelIds={channelIds} currentUser={address} />
    </div>
  )
}

function ChannelList({
  managerAddress,
  channelIds,
  currentUser,
}: {
  managerAddress: `0x${string}`
  channelIds: number[]
  currentUser: string | undefined
}) {
  return (
    <div className="rounded-xl bg-white/5 p-6">
      <h3 className="text-lg font-semibold text-white mb-4">Channels ({channelIds.length})</h3>
      <div className="space-y-3">
        {channelIds.length === 0 && <p className="text-white/60 text-sm">No channels yet.</p>}
        {channelIds.map((idx) => (
          <ChannelRow key={idx} managerAddress={managerAddress} index={idx} currentUser={currentUser} />
        ))}
      </div>
    </div>
  )
}

function ChannelRow({
  managerAddress,
  index,
  currentUser,
}: {
  managerAddress: `0x${string}`
  index: number
  currentUser: string | undefined
}) {
  const { data: channelId } = useReadContract({
    address: managerAddress,
    abi: PAYMENT_CHANNEL_MANAGER_ABI,
    functionName: 'getChannelIdByIndex',
    args: [BigInt(index)],
  })
  const { data: ch } = useReadContract({
    address: managerAddress,
    abi: PAYMENT_CHANNEL_MANAGER_ABI,
    functionName: 'getChannel',
    args: channelId !== undefined ? [channelId] : undefined,
  })
  if (ch == null || channelId == null) return null
  const status = Number(ch.status)
  const statusLabel = CHANNEL_STATUS[status] ?? 'Unknown'
  const isMine = currentUser && (ch.participantA.toLowerCase() === currentUser.toLowerCase() || ch.participantB.toLowerCase() === currentUser.toLowerCase())
  return (
    <div className="rounded-lg border border-white/20 p-4 bg-black/20">
      <div className="flex flex-wrap items-center gap-2 text-sm">
        <span className="text-white/60">ID: {String(channelId)}</span>
        <span className="text-white/80">A: {ch.participantA.slice(0, 10)}...</span>
        <span className="text-white/80">B: {ch.participantB.slice(0, 10)}...</span>
        <span className="text-white/80">Deposits: {(Number(ch.depositA) / 1e18).toFixed(4)} / {(Number(ch.depositB) / 1e18).toFixed(4)} ETH</span>
        <span className={status === 1 ? 'text-green-400' : status === 2 ? 'text-amber-400' : 'text-white/60'}>
          {statusLabel}
        </span>
        {isMine && <span className="text-blue-300">(yours)</span>}
      </div>
      {status === 2 && ch.disputeDeadline > 0n && (
        <p className="text-amber-200 text-xs mt-1">
          Dispute deadline: {new Date(Number(ch.disputeDeadline) * 1000).toISOString()}
        </p>
      )}
    </div>
  )
}
