"use client";

import { useState } from "react";
import { useAccount, useSignMessage, useSignTypedData } from "wagmi";
import type { CalldataWarning, DecodedCalldata, PendingRequest, SimulationResult, TransactionParams } from "@/lib/types";
import { formatEther, formatUnits, type Hex } from "viem";

interface RequestCardProps {
  request: PendingRequest;
  onRespond: (id: string, result?: string, error?: { code: number; message: string }) => void;
}

export default function RequestCard({ request, onRespond }: RequestCardProps) {
  const [signing, setSigning] = useState(false);
  const { address, connector } = useAccount();
  const { signMessageAsync } = useSignMessage();
  const { signTypedDataAsync } = useSignTypedData();

  const handleApprove = async () => {
    if (!address) return;
    setSigning(true);

    try {
      let result: string;

      switch (request.method) {
        case "eth_sendTransaction": {
          if (!connector) throw new Error("Wallet not connected");
          const txParams = (Array.isArray(request.params) ? request.params[0] : request.params) as TransactionParams;
          // Get the raw EIP-1193 provider (e.g. window.ethereum) directly from
          // the connector, fully bypassing viem/wagmi's transport layer
          const provider = await connector.getProvider() as { request: (args: { method: string; params: unknown[] }) => Promise<string> };
          // Strip undefined/null/empty fields — MetaMask rejects keys like `to: undefined`
          // (contract deployments legitimately have no `to`)
          // Pass through all fields from the original request as-is,
          // only omitting keys that are truly absent (undefined/null).
          // Map `input` → `data` (forge/geth use `input`, MetaMask expects `data`)
          const raw: Record<string, unknown> = { from: address, ...txParams };
          if (raw.input && !raw.data) {
            raw.data = raw.input;
          }
          delete raw.input;
          const tx: Record<string, string> = {};
          for (const [k, v] of Object.entries(raw)) {
            if (v != null && v !== "") tx[k] = String(v);
          }
          console.log("[cipher-gate] eth_sendTransaction params:", JSON.stringify(tx, null, 2));
          result = await provider.request({
            method: "eth_sendTransaction",
            params: [tx],
          });
          break;
        }
        case "personal_sign": {
          const params = request.params as string[];
          // personal_sign params: [message, address]
          const message = params[0];
          result = await signMessageAsync({ message: { raw: message as Hex } });
          break;
        }
        case "eth_sign": {
          const params = request.params as string[];
          // eth_sign params: [address, message]
          const message = params[1];
          result = await signMessageAsync({ message: { raw: message as Hex } });
          break;
        }
        case "eth_signTypedData":
        case "eth_signTypedData_v3":
        case "eth_signTypedData_v4": {
          const params = request.params as [string, string | object];
          const typedData = typeof params[1] === "string" ? JSON.parse(params[1]) : params[1];
          result = await signTypedDataAsync({
            domain: typedData.domain,
            types: typedData.types,
            primaryType: typedData.primaryType,
            message: typedData.message,
          });
          break;
        }
        case "eth_signTransaction": {
          // eth_signTransaction is less commonly supported by wallets via wagmi
          // We'll return an error suggesting eth_sendTransaction instead
          onRespond(request.id, undefined, {
            code: -32601,
            message: "eth_signTransaction not supported. Use eth_sendTransaction instead.",
          });
          setSigning(false);
          return;
        }
        default:
          onRespond(request.id, undefined, {
            code: -32601,
            message: `Unsupported method: ${request.method}`,
          });
          setSigning(false);
          return;
      }

      onRespond(request.id, result);
    } catch (err: unknown) {
      const message = err instanceof Error ? err.message : "User rejected or signing failed";
      onRespond(request.id, undefined, { code: 4001, message });
    } finally {
      setSigning(false);
    }
  };

  const handleReject = () => {
    onRespond(request.id, undefined, { code: 4001, message: "User rejected the request" });
  };

  return (
    <div className="rounded-xl border border-zinc-200 bg-white p-5 shadow-sm dark:border-zinc-800 dark:bg-zinc-900">
      {/* Header */}
      <div className="mb-4 flex items-center justify-between">
        <div className="flex items-center gap-2">
          <span className="rounded-md bg-amber-100 px-2.5 py-1 text-xs font-semibold text-amber-800 dark:bg-amber-900/30 dark:text-amber-400">
            {request.method}
          </span>
          <span className="text-xs text-zinc-400">
            {new Date(request.receivedAt).toLocaleTimeString()}
          </span>
        </div>
        <span className="font-mono text-xs text-zinc-400">
          {request.id.slice(0, 8)}...
        </span>
      </div>

      {/* Params */}
      <div className="mb-4">
        <ParamsDisplay method={request.method} params={request.params} decodedCalldata={request.decoded_calldata} />
      </div>

      {/* Simulation Result */}
      {request.simulation && (
        <div className="mb-4">
          <SimulationDisplay simulation={request.simulation} />
        </div>
      )}

      {/* Actions */}
      <div className="flex gap-3">
        <button
          onClick={handleReject}
          disabled={signing}
          className="flex-1 rounded-lg border border-zinc-200 px-4 py-2.5 text-sm font-medium text-zinc-700 transition-colors hover:bg-zinc-50 disabled:opacity-50 dark:border-zinc-700 dark:text-zinc-300 dark:hover:bg-zinc-800"
        >
          Reject
        </button>
        <button
          onClick={handleApprove}
          disabled={signing || !address || !connector}
          className="flex-1 rounded-lg bg-emerald-600 px-4 py-2.5 text-sm font-medium text-white transition-colors hover:bg-emerald-500 disabled:opacity-50"
        >
          {signing ? "Signing..." : "Approve & Sign"}
        </button>
      </div>
    </div>
  );
}

function ParamsDisplay({ method, params, decodedCalldata }: { method: string; params: unknown; decodedCalldata?: DecodedCalldata }) {
  if (method === "eth_sendTransaction" || method === "eth_signTransaction") {
    const tx = (Array.isArray(params) ? params[0] : params) as TransactionParams;
    const calldata = tx.data || tx.input;
    const isDeployment = !tx.to;
    return (
      <div className="space-y-2 rounded-lg bg-zinc-50 p-3 dark:bg-zinc-800/50">
        {tx.nonce && (
          <Row label="Nonce" value={BigInt(tx.nonce).toString()} mono />
        )}
        {isDeployment && (
          <Row label="Type" value="Contract Deployment" />
        )}
        {tx.to && (
          <Row label="To" value={tx.to} mono />
        )}
        {tx.value && tx.value !== "0x0" && (
          <Row label="Value" value={`${formatEther(BigInt(tx.value))} ETH`} />
        )}
        {calldata && calldata !== "0x" && (
          decodedCalldata ? (
            <DecodedCalldataDisplay decoded={decodedCalldata} />
          ) : (
            <Row
              label={isDeployment ? "Bytecode" : "Data"}
              value={calldata.length > 66 ? `${calldata.slice(0, 66)}...` : calldata}
              mono
            />
          )
        )}
        {tx.gas && (
          <Row label="Gas Limit" value={BigInt(tx.gas).toString()} />
        )}
      </div>
    );
  }

  if (method === "personal_sign") {
    const msg = (params as string[])[0];
    let decoded: string;
    try {
      // Try to decode hex to utf-8
      decoded = Buffer.from(msg.replace("0x", ""), "hex").toString("utf-8");
    } catch {
      decoded = msg;
    }
    return (
      <div className="rounded-lg bg-zinc-50 p-3 dark:bg-zinc-800/50">
        <Row label="Message" value={decoded} />
      </div>
    );
  }

  if (method.startsWith("eth_signTypedData")) {
    const raw = (params as [string, string | object])[1];
    const typedData = typeof raw === "string" ? JSON.parse(raw) : raw;
    return (
      <div className="rounded-lg bg-zinc-50 p-3 dark:bg-zinc-800/50">
        <p className="mb-1 text-xs font-medium text-zinc-500 dark:text-zinc-400">
          Typed Data ({typedData.primaryType})
        </p>
        <pre className="max-h-48 overflow-auto whitespace-pre-wrap text-xs text-zinc-700 dark:text-zinc-300">
          {JSON.stringify(typedData.message, null, 2)}
        </pre>
      </div>
    );
  }

  // Fallback: show raw JSON
  return (
    <div className="rounded-lg bg-zinc-50 p-3 dark:bg-zinc-800/50">
      <pre className="max-h-48 overflow-auto whitespace-pre-wrap text-xs text-zinc-700 dark:text-zinc-300">
        {JSON.stringify(params, null, 2)}
      </pre>
    </div>
  );
}

function formatParamValue(
  param: { sol_type: string; value: string },
  tokenMeta?: { symbol: string; decimals: number },
): string {
  // Render ERC20 amounts using the token's decimals + symbol.
  if (tokenMeta && param.sol_type.startsWith("uint")) {
    try {
      return `${formatUnits(BigInt(param.value), tokenMeta.decimals)} ${tokenMeta.symbol}`;
    } catch {
      // fall through to default rendering
    }
  }
  if (param.sol_type === "uint256" && param.value.length > 20) {
    return `${param.value.slice(0, 20)}...`;
  }
  return param.value;
}

function DecodedCalldataDisplay({ decoded }: { decoded: DecodedCalldata }) {
  return (
    <div className="space-y-2">
      {/* Function badge + selector */}
      <div className="flex items-center gap-2">
        <span className="rounded bg-blue-100 px-2 py-0.5 text-xs font-semibold text-blue-800 dark:bg-blue-900/30 dark:text-blue-400">
          {decoded.function_name}
        </span>
        {decoded.abi_verified && (
          <span className="rounded bg-emerald-100 px-1.5 py-0.5 text-[10px] font-semibold text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400">
            ✓ verified ABI
          </span>
        )}
        <span className="font-mono text-xs text-zinc-400">{decoded.selector}</span>
      </div>

      {/* Decoded parameters — show real names when available */}
      {decoded.params.map((param, i) => {
        const named = param.name && !/^param\d+$/.test(param.name);
        return (
          <Row
            key={i}
            label={named ? `${param.name} (${param.sol_type})` : param.sol_type}
            value={formatParamValue(param, decoded.token_meta)}
            mono={param.sol_type === "address" || param.sol_type.startsWith("bytes")}
          />
        );
      })}

      {/* Warnings */}
      {decoded.warnings?.map((warning, i) => (
        <WarningBanner key={i} warning={warning} />
      ))}
    </div>
  );
}

function WarningBanner({ warning }: { warning: CalldataWarning }) {
  const message =
    warning.type === "unlimited_approval"
      ? `Unlimited token approval to ${warning.spender.slice(0, 10)}...${warning.spender.slice(-8)}`
      : `Full collection approval to ${warning.operator.slice(0, 10)}...${warning.operator.slice(-8)}`;

  return (
    <div className="flex items-center gap-2 rounded-lg bg-red-50 px-3 py-2 dark:bg-red-900/20">
      <svg className="h-4 w-4 shrink-0 text-red-600 dark:text-red-400" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor">
        <path strokeLinecap="round" strokeLinejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z" />
      </svg>
      <span className="text-xs font-medium text-red-700 dark:text-red-300">
        {message}
      </span>
    </div>
  );
}

function SimulationDisplay({ simulation }: { simulation: SimulationResult }) {
  if (simulation.success) {
    return (
      <div className="flex items-center gap-2 rounded-lg bg-emerald-50 px-3 py-2.5 dark:bg-emerald-900/20">
        <svg className="h-4 w-4 shrink-0 text-emerald-600 dark:text-emerald-400" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor">
          <path strokeLinecap="round" strokeLinejoin="round" d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
        </svg>
        <span className="text-xs font-medium text-emerald-700 dark:text-emerald-300">
          Simulation passed
        </span>
        {simulation.gas_estimate && (
          <span className="ml-auto font-mono text-xs text-emerald-600 dark:text-emerald-400">
            Gas: {BigInt(simulation.gas_estimate).toLocaleString()}
          </span>
        )}
      </div>
    );
  }

  return (
    <div className="rounded-lg bg-red-50 px-3 py-2.5 dark:bg-red-900/20">
      <div className="flex items-center gap-2">
        <svg className="h-4 w-4 shrink-0 text-red-600 dark:text-red-400" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor">
          <path strokeLinecap="round" strokeLinejoin="round" d="M12 9v3.75m9-.75a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 3.75h.008v.008H12v-.008Z" />
        </svg>
        <span className="text-xs font-medium text-red-700 dark:text-red-300">
          Simulation reverted
        </span>
      </div>
      {simulation.revert_reason && (
        <p className="mt-1.5 pl-6 font-mono text-xs text-red-600 dark:text-red-400">
          {simulation.revert_reason}
        </p>
      )}
    </div>
  );
}

function Row({ label, value, mono }: { label: string; value: string; mono?: boolean }) {
  return (
    <div className="flex items-baseline justify-between gap-4">
      <span className="shrink-0 text-xs font-medium text-zinc-500 dark:text-zinc-400">
        {label}
      </span>
      <span
        className={`truncate text-sm text-zinc-800 dark:text-zinc-200 ${
          mono ? "font-mono" : ""
        }`}
      >
        {value}
      </span>
    </div>
  );
}
