import { AnimatePresence, motion } from "framer-motion";
import type { HyperliquidWallet } from "hypurr-grpc/ts/hypurr/wallet";
import { useCallback, useState, type ReactNode } from "react";
import { useHypurrConnectInternal } from "./HypurrConnectProvider";
import {
  AGENT_APPROVAL_DURATION_OPTIONS,
  DEFAULT_AGENT_APPROVAL_DURATION_MS,
  createTelegramAgentApprovalName,
  type AgentApprovalDurationOption,
} from "./agentWallet";
import {
  AlertTriangle,
  Check,
  Eye,
  EyeOff,
  KeyRound,
  Loader2,
  Plus,
  SpinKeyframes,
  Wallet,
  X,
} from "./icons/lucide";
import type { Hex, SignTypedDataFn } from "./types";

export interface AddWalletModalProps {
  isOpen: boolean;
  onClose: () => void;
  ownerAddress?: string | null;
  isWalletConnected?: boolean;
  chainId?: number;
  signTypedDataAsync?: SignTypedDataFn;
  onConnectWallet?: () => void;
  onDisconnectWallet?: () => void;
  onWalletAdded?: (wallet?: HyperliquidWallet) => void | Promise<void>;
  onAddReadOnlyWallet?: (address: string) => void | Promise<void>;
  onNotify?: (n: { type: "success" | "error"; message: string }) => void;
  hyperliquidChain?: "Mainnet" | "Testnet" | string;
  /** Expiration choices for owner-approved agent wallets. Defaults to 1/7/30/90 days. */
  agentApprovalDurationOptions?: AgentApprovalDurationOption[];
  /** Initial owner approval duration. Defaults to 1 day. */
  defaultAgentApprovalDurationMs?: number;
}

type TabKey = "generate" | "import" | "api" | "readonly";

const WALLET_NAME_REGEX = /^[a-zA-Z0-9/]*$/;
const PRIVATE_KEY_REGEX = /^(0x)?[a-fA-F0-9]{64}$/;
const BASE_TAB_KEYS: TabKey[] = ["generate", "import", "api"];

function createAuthSignatureRequest(
  chainId: number,
  hyperliquidChain: string,
  agentAddress: string,
  approvalDurationMs: number,
) {
  const nonce = Date.now();

  return {
    domain: {
      name: "HyperliquidSignTransaction",
      version: "1",
      chainId,
      verifyingContract: "0x0000000000000000000000000000000000000000" as const,
    },
    types: {
      "HyperliquidTransaction:ApproveAgent": [
        { name: "hyperliquidChain", type: "string" },
        { name: "agentAddress", type: "address" },
        { name: "agentName", type: "string" },
        { name: "nonce", type: "uint64" },
      ],
    },
    primaryType: "HyperliquidTransaction:ApproveAgent",
    message: {
      hyperliquidChain,
      agentAddress: agentAddress.toLowerCase() as Hex,
      agentName: createTelegramAgentApprovalName(approvalDurationMs),
      nonce,
    },
  };
}

const tabClass = (selected: boolean) =>
  `flex-1 py-1.5 rounded text-base font-medium flex items-center justify-center gap-1.5 ${
    selected ? "btn-raised-active" : "btn-raised"
  }`;

const inputClass = (hasError?: boolean) =>
  `input ${
    hasError ? "input-error" : ""
  } w-full rounded-lg px-3 py-2.5 text-content placeholder-content-faint focus:outline-none font-mono text-base`;

const formatAddress = (address: string) =>
  `${address.slice(0, 6)}...${address.slice(-4)}`;

export function AddWalletModal({
  isOpen,
  onClose,
  ownerAddress,
  isWalletConnected,
  chainId,
  signTypedDataAsync,
  onConnectWallet,
  onDisconnectWallet,
  onWalletAdded,
  onAddReadOnlyWallet,
  onNotify,
  hyperliquidChain = "Mainnet",
  agentApprovalDurationOptions,
  defaultAgentApprovalDurationMs = DEFAULT_AGENT_APPROVAL_DURATION_MS,
}: AddWalletModalProps): ReactNode {
  const tabKeys = onAddReadOnlyWallet
    ? [...BASE_TAB_KEYS, "readonly" as const]
    : BASE_TAB_KEYS;

  const [activeTab, setActiveTab] = useState<TabKey>("generate");
  const [isLoading, setIsLoading] = useState(false);
  const [walletName, setWalletName] = useState("");
  const [newWalletName, setNewWalletName] = useState("");
  const [importWalletName, setImportWalletName] = useState("");
  const [importPrivateKey, setImportPrivateKey] = useState("");
  const [showImportPrivateKey, setShowImportPrivateKey] = useState(false);
  const [readOnlyAddress, setReadOnlyAddress] = useState("");
  const [agentApprovalDurationMs, setAgentApprovalDurationMs] = useState(
    defaultAgentApprovalDurationMs,
  );
  const [error, setError] = useState<string | null>(null);

  const {
    authMethod,
    createWallet,
    isLoading: userLoading,
    refreshWallets,
    telegramClient,
    telegramRpcOptions,
  } = useHypurrConnectInternal();

  const isValidWalletName =
    walletName.length > 0 && WALLET_NAME_REGEX.test(walletName);
  const isValidNewWalletName =
    newWalletName.length > 0 && WALLET_NAME_REGEX.test(newWalletName);
  const isValidImportWalletName =
    importWalletName.length > 0 && WALLET_NAME_REGEX.test(importWalletName);
  const trimmedImportPrivateKey = importPrivateKey.trim();
  const isValidImportPrivateKey = PRIVATE_KEY_REGEX.test(
    trimmedImportPrivateKey,
  );
  const isProcessing = isLoading || userLoading;
  const durationOptions =
    agentApprovalDurationOptions && agentApprovalDurationOptions.length > 0
      ? agentApprovalDurationOptions
      : AGENT_APPROVAL_DURATION_OPTIONS;

  const handleClose = useCallback(() => {
    if (isProcessing) return;
    setError(null);
    setImportPrivateKey("");
    setShowImportPrivateKey(false);
    onClose();
  }, [isProcessing, onClose]);

  const assertTelegramReady = useCallback(() => {
    if (authMethod !== "telegram" || !telegramRpcOptions) {
      throw new Error("Telegram login is required to add wallets");
    }
  }, [authMethod, telegramRpcOptions]);

  const completeWalletAdded = useCallback(
    async (wallet?: HyperliquidWallet) => {
      refreshWallets();
      await onWalletAdded?.(wallet);
    },
    [onWalletAdded, refreshWallets],
  );

  const handleCreateWallet = useCallback(async () => {
    if (!isValidNewWalletName) {
      setError("Wallet name may only contain letters, numbers, and /");
      return;
    }
    setError(null);
    setIsLoading(true);
    try {
      assertTelegramReady();
      const wallet = await createWallet(newWalletName);
      await completeWalletAdded(wallet);
      onNotify?.({ type: "success", message: "Wallet created successfully" });
      setNewWalletName("");
      handleClose();
    } catch (e: unknown) {
      const message =
        e instanceof Error ? e.message : "Failed to create wallet";
      setError(message);
      onNotify?.({ type: "error", message });
    } finally {
      setIsLoading(false);
    }
  }, [
    assertTelegramReady,
    completeWalletAdded,
    createWallet,
    handleClose,
    isValidNewWalletName,
    newWalletName,
    onNotify,
  ]);

  const handleImportWallet = useCallback(async () => {
    if (!isValidImportWalletName) {
      setError("Wallet name may only contain letters, numbers, and /");
      return;
    }
    if (!isValidImportPrivateKey) {
      setError("Please enter a valid 64-character hex private key");
      return;
    }
    setError(null);
    setIsLoading(true);
    try {
      assertTelegramReady();
      const response = await telegramClient.hyperliquidWalletImport(
        {
          authData: {},
          name: importWalletName,
          privateKey: trimmedImportPrivateKey,
        },
        telegramRpcOptions,
      );
      await completeWalletAdded(response.response.wallet);
      onNotify?.({ type: "success", message: "Wallet imported successfully" });
      setImportWalletName("");
      setImportPrivateKey("");
      handleClose();
    } catch (e: unknown) {
      const message =
        e instanceof Error ? e.message : "Failed to import wallet";
      setError(message);
      onNotify?.({ type: "error", message });
    } finally {
      setIsLoading(false);
    }
  }, [
    assertTelegramReady,
    completeWalletAdded,
    handleClose,
    importWalletName,
    isValidImportPrivateKey,
    isValidImportWalletName,
    onNotify,
    telegramClient,
    telegramRpcOptions,
    trimmedImportPrivateKey,
  ]);

  const handleSignAndAdd = useCallback(async () => {
    if (!ownerAddress) {
      onConnectWallet?.();
      return;
    }
    if (!signTypedDataAsync || !chainId) {
      setError("Wallet signing is not ready yet");
      return;
    }
    if (!isValidWalletName) {
      setError("Wallet name may only contain letters, numbers, and /");
      return;
    }
    setError(null);
    setIsLoading(true);
    try {
      assertTelegramReady();
      const res = await telegramClient.hyperliquidAgentSignatureCreate(
        { authData: {}, address: ownerAddress },
        telegramRpcOptions,
      );
      const agentAddress = res.response.agent;
      const request = createAuthSignatureRequest(
        chainId,
        hyperliquidChain,
        agentAddress,
        agentApprovalDurationMs,
      );
      const signature = await signTypedDataAsync(request);

      await telegramClient.hyperliquidAgentWalletCreate(
        {
          authData: {},
          name: walletName,
          signature: {
            agentAddress: request.message.agentAddress,
            agentName: request.message.agentName,
            nonce: request.message.nonce,
            chainId,
            signature,
          },
        },
        telegramRpcOptions,
      );

      await completeWalletAdded();
      onNotify?.({ type: "success", message: "Wallet added successfully" });
      onDisconnectWallet?.();
      setWalletName("");
      handleClose();
    } catch (e: unknown) {
      const message = e instanceof Error ? e.message : "Failed to add wallet";
      setError(message);
      onNotify?.({ type: "error", message });
    } finally {
      setIsLoading(false);
    }
  }, [
    assertTelegramReady,
    agentApprovalDurationMs,
    chainId,
    completeWalletAdded,
    handleClose,
    hyperliquidChain,
    isValidWalletName,
    onConnectWallet,
    onDisconnectWallet,
    onNotify,
    ownerAddress,
    signTypedDataAsync,
    telegramClient,
    telegramRpcOptions,
    walletName,
  ]);

  const handleAddReadOnly = useCallback(async () => {
    const trimmed = readOnlyAddress.trim();
    if (!trimmed) {
      setError("Please enter a wallet address");
      return;
    }
    if (!/^0x[a-fA-F0-9]{40}$/.test(trimmed)) {
      setError("Invalid Ethereum address format");
      return;
    }
    setError(null);
    setIsLoading(true);
    try {
      await onAddReadOnlyWallet?.(trimmed);
      onNotify?.({ type: "success", message: "Read-only wallet added" });
      setReadOnlyAddress("");
      handleClose();
    } catch (e: unknown) {
      const message = e instanceof Error ? e.message : "Failed to add wallet";
      setError(message);
      onNotify?.({ type: "error", message });
    } finally {
      setIsLoading(false);
    }
  }, [handleClose, onAddReadOnlyWallet, onNotify, readOnlyAddress]);

  return (
    <AnimatePresence>
      {isOpen && (
        <motion.div className="hypurr-connect" style={{ display: "contents" }}>
          <SpinKeyframes />
          <motion.div
            className="fixed inset-0 z-[100] bg-overlay/70 backdrop-blur-sm"
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
            transition={{ duration: 0.15 }}
            onClick={handleClose}
          />
          <div
            className="fixed inset-0 z-[101] flex items-center justify-center p-4"
            onClick={handleClose}
          >
            <motion.div
              className="relative w-full max-w-md overflow-hidden rounded-lg border border-line bg-surface-modal font-sans shadow-modal"
              initial={{ opacity: 0, y: 8 }}
              animate={{ opacity: 1, y: 0 }}
              exit={{ opacity: 0, y: 8 }}
              transition={{ duration: 0.18, ease: "easeOut" }}
              onClick={(event) => event.stopPropagation()}
            >
              <div className="relative flex items-center justify-center border-b border-line px-6 pb-5 pt-6">
                <h3 className="text-lg font-medium text-content">Add Wallet</h3>
                <button
                  onClick={handleClose}
                  disabled={isProcessing}
                  className="absolute right-6 text-content-muted transition-colors hover:text-content disabled:cursor-not-allowed disabled:opacity-40"
                  aria-label="Close"
                >
                  <X size={16} />
                </button>
              </div>

              <div className="flex gap-1.5 px-6 pt-5">
                {tabKeys.map((tab) => (
                  <button
                    key={tab}
                    type="button"
                    onClick={() => {
                      setError(null);
                      setActiveTab(tab);
                    }}
                    className={tabClass(activeTab === tab)}
                  >
                    {tab === "generate" ? (
                      <>
                        <Plus size={13} /> New
                      </>
                    ) : tab === "import" ? (
                      <>
                        <KeyRound size={13} /> Import
                      </>
                    ) : tab === "api" ? (
                      <>
                        <Wallet size={13} /> Link
                      </>
                    ) : (
                      <>
                        <Eye size={13} /> Watch
                      </>
                    )}
                  </button>
                ))}
              </div>

              <div className="px-6 pb-6 pt-5">
                {activeTab === "generate" && (
                  <section className="space-y-4">
                    <p className="text-base text-content-muted">
                      Create a new Hyperliquid wallet.
                    </p>
                    <WalletNameInput
                      value={newWalletName}
                      setValue={setNewWalletName}
                      isValid={isValidNewWalletName}
                      clearError={() => setError(null)}
                      placeholder="MyNewWallet"
                    />
                    <button
                      onClick={handleCreateWallet}
                      disabled={isProcessing || !isValidNewWalletName}
                      className={`flex w-full items-center justify-center gap-2 rounded-lg py-2 text-base font-medium ${
                        !isProcessing && isValidNewWalletName
                          ? "btn-raised"
                          : "btn-raised-disabled"
                      }`}
                    >
                      {isProcessing ? (
                        <>
                          <Loader2 size={14} /> Creating...
                        </>
                      ) : (
                        <>
                          <Plus size={14} /> Create Wallet
                        </>
                      )}
                    </button>
                    {error && <ErrorBox message={error} />}
                  </section>
                )}

                {activeTab === "import" && (
                  <section className="space-y-4">
                    <p className="text-base text-content-muted">
                      Import a Hyperliquid wallet with its private key.
                    </p>
                    <WalletNameInput
                      value={importWalletName}
                      setValue={setImportWalletName}
                      isValid={isValidImportWalletName}
                      clearError={() => setError(null)}
                      placeholder="MyImportedWallet"
                    />
                    <div>
                      <label className="mb-2 block text-base uppercase tracking-[0.1em] text-content-muted">
                        Private Key
                      </label>
                      <div className="relative">
                        <input
                          type={showImportPrivateKey ? "text" : "password"}
                          value={importPrivateKey}
                          onChange={(event) => {
                            setImportPrivateKey(event.target.value);
                            setError(null);
                          }}
                          placeholder="0x..."
                          spellCheck={false}
                          autoComplete="off"
                          className={`${inputClass(
                            !!importPrivateKey && !isValidImportPrivateKey,
                          )} pr-11`}
                        />
                        <button
                          type="button"
                          onClick={() =>
                            setShowImportPrivateKey((visible) => !visible)
                          }
                          className="absolute right-2 top-1/2 -translate-y-1/2 p-1.5 text-content-subtle transition-colors hover:text-content-tertiary"
                          aria-label={
                            showImportPrivateKey
                              ? "Hide private key"
                              : "Show private key"
                          }
                        >
                          {showImportPrivateKey ? (
                            <EyeOff size={15} />
                          ) : (
                            <Eye size={15} />
                          )}
                        </button>
                      </div>
                    </div>
                    <button
                      onClick={handleImportWallet}
                      disabled={
                        isProcessing ||
                        !isValidImportWalletName ||
                        !isValidImportPrivateKey
                      }
                      className={`flex w-full items-center justify-center gap-2 rounded-lg py-2 text-base font-medium ${
                        !isProcessing &&
                        isValidImportWalletName &&
                        isValidImportPrivateKey
                          ? "btn-raised-accent"
                          : "btn-raised-disabled"
                      }`}
                    >
                      {isProcessing ? (
                        <>
                          <Loader2 size={14} /> Importing...
                        </>
                      ) : (
                        <>
                          <KeyRound size={14} /> Import Wallet
                        </>
                      )}
                    </button>
                    {error && <ErrorBox message={error} />}
                  </section>
                )}

                {activeTab === "api" && (
                  <section className="space-y-4">
                    <p className="text-base text-content-muted">
                      Connect your wallet and sign to link it for trading.
                    </p>
                    {!isWalletConnected || !ownerAddress ? (
                      <button
                        onClick={onConnectWallet}
                        className="btn-raised flex w-full items-center justify-center gap-2 rounded-lg py-2 text-base font-medium"
                      >
                        <Wallet size={14} /> Connect Wallet
                      </button>
                    ) : (
                      <>
                        <div className="flex items-center gap-3 rounded-lg border border-line bg-surface-btn px-3 py-2.5">
                          <div className="flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-md border border-line-strong bg-surface-btn-hover">
                            <Wallet size={14} className="text-content-muted" />
                          </div>
                          <div className="min-w-0 flex-1">
                            <p className="text-base text-content-muted">
                              Connected
                            </p>
                            <p className="font-mono text-base text-content">
                              {formatAddress(ownerAddress)}
                            </p>
                          </div>
                          <Check
                            size={16}
                            className="flex-shrink-0 text-trade-up"
                          />
                        </div>
                        <WalletNameInput
                          value={walletName}
                          setValue={setWalletName}
                          isValid={isValidWalletName}
                          clearError={() => setError(null)}
                          placeholder="MyWallet"
                        />
                        <ApprovalDurationPicker
                          value={agentApprovalDurationMs}
                          onChange={setAgentApprovalDurationMs}
                          options={durationOptions}
                          disabled={isProcessing}
                        />
                        <button
                          onClick={handleSignAndAdd}
                          disabled={isProcessing || !isValidWalletName}
                          className={`flex w-full items-center justify-center gap-2 rounded-lg py-2 text-base font-medium ${
                            !isProcessing && isValidWalletName
                              ? "btn-raised"
                              : "btn-raised-disabled"
                          }`}
                        >
                          {isProcessing ? (
                            <>
                              <Loader2 size={14} /> Signing...
                            </>
                          ) : (
                            "Sign & Add Wallet"
                          )}
                        </button>
                        {onDisconnectWallet && (
                          <button
                            onClick={onDisconnectWallet}
                            className="w-full py-1.5 text-base text-content-muted transition-colors hover:text-content-tertiary"
                          >
                            Disconnect & use different wallet
                          </button>
                        )}
                      </>
                    )}
                    {error && <ErrorBox message={error} />}
                  </section>
                )}

                {activeTab === "readonly" && onAddReadOnlyWallet && (
                  <section className="space-y-4">
                    <p className="text-base text-content-muted">
                      View positions and balances without trading access.
                    </p>
                    <div>
                      <label className="mb-2 block text-base uppercase tracking-[0.1em] text-content-muted">
                        Wallet Address
                      </label>
                      <input
                        type="text"
                        value={readOnlyAddress}
                        onChange={(event) =>
                          setReadOnlyAddress(event.target.value)
                        }
                        placeholder="0x..."
                        className={inputClass()}
                      />
                    </div>
                    <button
                      onClick={handleAddReadOnly}
                      disabled={isProcessing || !readOnlyAddress.trim()}
                      className={`flex w-full items-center justify-center gap-2 rounded-lg py-2 text-base font-medium ${
                        !isProcessing && readOnlyAddress.trim()
                          ? "btn-raised"
                          : "btn-raised-disabled"
                      }`}
                    >
                      {isProcessing ? (
                        <>
                          <Loader2 size={14} /> Adding...
                        </>
                      ) : (
                        <>
                          <Eye size={14} /> Add Watch Wallet
                        </>
                      )}
                    </button>
                    {error && <ErrorBox message={error} />}
                  </section>
                )}
              </div>
            </motion.div>
          </div>
        </motion.div>
      )}
    </AnimatePresence>
  );
}

function WalletNameInput({
  value,
  setValue,
  isValid,
  clearError,
  placeholder,
}: {
  value: string;
  setValue: (value: string) => void;
  isValid: boolean;
  clearError: () => void;
  placeholder: string;
}) {
  return (
    <div>
      <label className="mb-2 block text-base uppercase tracking-[0.1em] text-content-muted">
        Wallet Name
      </label>
      <input
        type="text"
        value={value}
        onChange={(event) => {
          const nextValue = event.target.value;
          if (WALLET_NAME_REGEX.test(nextValue) || nextValue === "") {
            setValue(nextValue);
            clearError();
          }
        }}
        placeholder={placeholder}
        className={inputClass(!!value && !isValid)}
      />
      <p className="mt-1.5 text-base text-content-faint">
        Letters, numbers, and / only
      </p>
    </div>
  );
}

function ApprovalDurationPicker({
  value,
  onChange,
  options,
  disabled,
}: {
  value: number;
  onChange: (value: number) => void;
  options: AgentApprovalDurationOption[];
  disabled?: boolean;
}) {
  return (
    <div>
      <label className="mb-2 block text-base uppercase tracking-[0.1em] text-content-muted">
        Approval Duration
      </label>
      <div className="grid grid-cols-4 gap-1.5">
        {options.map((option) => (
          <button
            key={`${option.label}-${option.durationMs}`}
            type="button"
            onClick={() => onChange(option.durationMs)}
            disabled={disabled}
            className={`rounded border py-1.5 text-base font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-50 ${
              value === option.durationMs
                ? "bg-surface-btn-active text-content border-surface-bd-active"
                : "bg-surface-btn text-content-muted border-surface-bd hover:bg-surface-btn-hover hover:border-surface-bd-hover hover:text-content-tertiary"
            }`}
          >
            {option.label}
          </button>
        ))}
      </div>
    </div>
  );
}

function ErrorBox({ message }: { message: string }) {
  return (
    <div className="flex items-start gap-2 rounded-lg border border-trade-down/20 bg-trade-down/[0.08] p-3">
      <AlertTriangle
        size={14}
        className="mt-0.5 flex-shrink-0 text-trade-down"
      />
      <p className="text-base text-trade-down">{message}</p>
    </div>
  );
}
