"use client";

import { useEffect, useRef, useState } from "react";
import type { FormEvent } from "react";
import Image from "next/image";
import { ethers } from "ethers";
import { LocaleSelector } from "@/components/LocaleSelector";
import { useI18n, type Locale, type MessageKey } from "@/hooks/useI18n";
import { useTheme } from "@/hooks/useTheme";

type StatusTone = "info" | "success" | "error";

type StatusState = {
  text: string;
  tone: StatusTone;
  key?: string;
  vars?: Record<string, string>;
};

type Eip1193Provider = {
  request: (args: { method: string; params?: unknown[] }) => Promise<unknown>;
  on?: (event: string, fn: (...args: unknown[]) => void | Promise<void>) => void;
  removeListener?: (event: string, fn: (...args: unknown[]) => void | Promise<void>) => void;
  providers?: Eip1193Provider[];
  isTrustWallet?: boolean;
  isTrust?: boolean;
};

type EthereumWindow = Window & {
  ethereum?: Eip1193Provider & { providers?: Eip1193Provider[] };
  trustwallet?: Eip1193Provider;
};

const formatChain = (chainId: number | null) => {
  if (chainId === 137) return "Polygon";
  if (typeof chainId === "number" && Number.isFinite(chainId)) return `Chain ${chainId}`;
  return "—";
};

type MessageEntry = {
  hash: string;
  to: string;
  from: string;
  message: string;
  createdAt: number;
};

export default function HomePage() {
  const { locale, setLocale, t, localeOptions } = useI18n("en");
  const [account, setAccount] = useState<string>("");
  const [chainId, setChainId] = useState<number | null>(null);
  const [txHash, setTxHash] = useState<string>("—");
  const [status, setStatus] = useState<StatusState>({
    text: t("status.waiting"),
    key: "status.waiting",
    tone: "info",
  });
  const [connecting, setConnecting] = useState<boolean>(false);
  const [sending, setSending] = useState<boolean>(false);
  const [connected, setConnected] = useState<boolean>(false);
  const [history, setHistory] = useState<MessageEntry[]>([]);

  const providerRef = useRef<Eip1193Provider | null>(null);
  const signerRef = useRef<ethers.Signer | null>(null);
  const formRef = useRef<HTMLFormElement | null>(null);

  const chainLabel = formatChain(chainId);
  const onPolygon = chainId === 137;
  const statusClass =
    status.tone === "success"
      ? "status-line status-ok"
      : status.tone === "error"
        ? "status-line status-error"
        : "status-line";
  const chainPillClass = onPolygon ? "pill pill-live" : "pill pill-quiet";
  const chainPillText = onPolygon ? "Polygon" : chainLabel || t("live.chainRequired");
  const disableSend = !connected || sending;
  const disableConnect = connecting;
  const disableDisconnect = !connected || connecting;
  const polygonscanBase = "https://polygonscan.com/tx/";
  const hasTxHash = Boolean(txHash && txHash !== "—");

  useEffect(() => {
    if (status.key) {
      setStatus((prev) => ({
        ...prev,
        text: t(prev.key as MessageKey, prev.vars),
      }));
    }
  }, [locale, status.key, status.vars, t]);

  function setStatusMessage(
    text: string,
    tone: StatusTone = "info",
    key?: string,
    vars?: Record<string, string>
  ) {
    setStatus({ text, tone, key, vars });
  }

  async function connectWallet() {
    if (connecting) return;
    setConnecting(true);
    setTxHash("—");
    setStatusMessage(t("status.preparingConnect"), "info", "status.preparingConnect");
    await disconnect(false);

    try {
      await connectInjected();
    } catch (err) {
      console.error("Connection failed", err);
      setStatusMessage(normalizeError(err, t("errors.connectionFailed"), t), "error");
      resetState();
    } finally {
      setConnecting(false);
    }
  }

  async function connectInjected() {
    const injected = pickInjectedProvider();
    if (!injected) {
      setStatusMessage(t("errors.noWallet"), "error", "errors.noWallet");
      return;
    }

    providerRef.current = injected;
    attachProviderEvents(injected);

    setStatusMessage(t("status.requesting"), "info", "status.requesting");
    // Try to force a fresh permission prompt when possible
    try {
      await injected.request({
        method: "wallet_requestPermissions",
        params: [{ eth_accounts: {} }],
      });
    } catch (err) {
      // Some wallets do not support this; fall back silently
      console.warn("wallet_requestPermissions not supported or rejected", err);
    }

    const accounts = (await injected.request({
      method: "eth_requestAccounts",
    })) as string[];
    if (!accounts || accounts.length === 0) {
      throw new Error(t("errors.noAccounts"));
    }

    const browserProvider = new ethers.BrowserProvider(
      injected as unknown as ethers.Eip1193Provider
    );
    const signer = await browserProvider.getSigner();
    signerRef.current = signer;
    const address = await signer.getAddress();
    const network = await browserProvider.getNetwork();

    setAccount(address);
    setChainId(Number(network.chainId));
    setConnected(true);
    setStatusMessage(t("status.connectedInjected"), "success", "status.connectedInjected");
  }

  async function disconnect(showMessage = true) {
    removeProviderEvents(providerRef.current);
    providerRef.current = null;
    signerRef.current = null;
    resetState();
    if (showMessage) setStatusMessage(t("status.disconnected"), "info", "status.disconnected");
  }

  function resetState() {
    setConnected(false);
    setAccount("");
    setChainId(null);
    setTxHash("—");
  }

  function attachProviderEvents(provider: Eip1193Provider) {
    if (!provider?.on) return;
    provider.on("accountsChanged", handleAccountsChanged);
    provider.on("chainChanged", handleChainChanged);
    provider.on("disconnect", handleDisconnectEvent);
  }

  function removeProviderEvents(provider: Eip1193Provider | null) {
    if (!provider?.removeListener) return;
    provider.removeListener("accountsChanged", handleAccountsChanged);
    provider.removeListener("chainChanged", handleChainChanged);
    provider.removeListener("disconnect", handleDisconnectEvent);
  }

  function handleDisconnectEvent() {
    disconnect(true);
  }

  async function handleAccountsChanged(...args: unknown[]) {
    const accounts = Array.isArray(args[0]) ? args[0] : [];
    if (!accounts || accounts.length === 0) {
      await disconnect(true);
      return;
    }
    const first = accounts.find((acct): acct is string => typeof acct === "string");
    if (!first) {
      await disconnect(true);
      return;
    }
    setAccount(first);
  }

  async function handleChainChanged(newChainId: unknown) {
    if (typeof newChainId !== "string" && typeof newChainId !== "number") return;
    const parsed = parseChainId(newChainId);
    setChainId(parsed);
  }

  async function ensurePolygonChain() {
    if (chainId === 137 || !providerRef.current) return true;
    try {
      await providerRef.current.request({
        method: "wallet_switchEthereumChain",
        params: [{ chainId: "0x89" }],
      });
      setChainId(137);
      return true;
    } catch (err) {
      console.warn("Chain switch rejected", err);
      return false;
    }
  }

  async function handleCopyHash() {
    if (!hasTxHash) return;
    try {
      if (!("clipboard" in navigator)) {
        throw new Error("Clipboard API unavailable");
      }
      await navigator.clipboard.writeText(txHash);
      setStatusMessage(t("live.copySuccess"), "success", "live.copySuccess");
    } catch (err) {
      console.warn("Copy failed", err);
      setStatusMessage(t("live.copyFailed"), "error", "live.copyFailed");
    }
  }

  async function handleSendMessage(event: FormEvent<HTMLFormElement>) {
    event.preventDefault();
    const formEl = event.currentTarget;
    setTxHash("—");
    if (!signerRef.current) {
      setStatusMessage(t("status.connectFirst"), "error", "status.connectFirst");
      return;
    }

    const formData = new FormData(event.currentTarget);
    const message = (formData.get("message") || "").toString().trim();
    let recipient = (formData.get("to") || "").toString().trim();

    if (!message) {
      setStatusMessage(t("status.messageEmpty"), "error", "status.messageEmpty");
      return;
    }
    if (!recipient) recipient = account;
    if (!ethers.isAddress(recipient)) {
      setStatusMessage(t("status.recipientInvalid"), "error", "status.recipientInvalid");
      return;
    }
    const recipientIsEoa = recipient.toLowerCase() === account.toLowerCase();
    if (recipientIsEoa) {
      setStatusMessage(t("status.eoaWarning"), "info", "status.eoaWarning");
    }

    const canUsePolygon = await ensurePolygonChain();
    if (!canUsePolygon) {
      setStatusMessage(t("status.switchPolygon"), "error", "status.switchPolygon");
      return;
    }

    try {
      setSending(true);
      setStatusMessage(t("status.prepareTx"), "info", "status.prepareTx");

      const data = ethers.hexlify(ethers.toUtf8Bytes(message));
      const txRequest = { to: recipient, value: 0n, data };
      let gasLimit: bigint;
      try {
        gasLimit = await signerRef.current.estimateGas(txRequest);
      } catch (estimateErr) {
        console.warn("Gas estimation failed, using fallback", estimateErr);
        setStatusMessage(t("status.gasFallback"), "info", "status.gasFallback");
        gasLimit = 250000n;
      }

      const feeData = await signerRef.current.provider?.getFeeData?.();
      const feeOverrides =
        feeData && feeData.maxFeePerGas && feeData.maxPriorityFeePerGas
          ? {
              maxFeePerGas: feeData.maxFeePerGas,
              maxPriorityFeePerGas: feeData.maxPriorityFeePerGas,
            }
          : {
              // conservative Polygon fallbacks in gwei
              maxFeePerGas: ethers.parseUnits("50", "gwei"),
              maxPriorityFeePerGas: ethers.parseUnits("40", "gwei"),
            };

      let tx;
      try {
        tx = await signerRef.current.sendTransaction({
          ...txRequest,
          gasLimit,
          ...feeOverrides,
        });
      } catch (sendErr) {
        const code = (sendErr as { code?: number | string })?.code;
        if (code === 4001 || code === "ACTION_REJECTED") {
          setStatusMessage(t("status.rejected"), "error", "status.rejected");
          throw sendErr;
        }

        // Fallback to legacy gasPrice if EIP-1559 style send fails or simulates a revert
        setStatusMessage(t("status.feeFallback"), "info", "status.feeFallback");
        const gasPrice =
          (await signerRef.current.provider?.getFeeData?.())?.gasPrice ||
          ethers.parseUnits("50", "gwei");
        tx = await signerRef.current.sendTransaction({
          ...txRequest,
          gasLimit,
          gasPrice,
        });
      }

      setTxHash(tx.hash);
      setStatusMessage(t("status.submitted", { hash: tx.hash }), "success", "status.submitted", {
        hash: tx.hash,
      });

      await tx.wait();
      setStatusMessage(t("status.onchain"), "success", "status.onchain");
      formEl.reset();
      setHistory((prev) => [
        {
          hash: tx.hash,
          to: recipient,
          from: account,
          message,
          createdAt: Date.now(),
        },
        ...prev,
      ]);
    } catch (err) {
      console.error("Send failed", err);
      setStatusMessage(normalizeError(err, t("errors.sendFailed"), t), "error");
    } finally {
      setSending(false);
    }
  }

  const { theme, toggleTheme } = useTheme();

  return (
    <main className="page">
      <header className="hero">
        <div className="hero-row">
          <div className="hero-text">
            <div className="hero-topline">
              <Image
                src="/logo.svg"
                alt="PolyNote logo"
                width={52}
                height={52}
                className="logo-mark"
              />
              <div className="eyebrow">{t("hero.eyebrow")}</div>
            </div>
            <h1>{t("hero.title")}</h1>
            <p className="lede">{t("hero.lede")}</p>
          </div>
          <div className="hero-actions">
            <LocaleSelector
              locale={locale}
              onChange={(code) => setLocale(code as Locale)}
              options={localeOptions}
              label={t("locale.label")}
              searchPlaceholder={t("locale.search")}
            />
            <button
              type="button"
              className="ghost theme-toggle"
              onClick={toggleTheme}
              aria-pressed={theme === "dark"}
              title={t("theme.toggle")}
            >
              {theme === "dark" ? "🌙" : "☀️"}{" "}
              {theme === "dark" ? t("theme.light") : t("theme.dark")}
            </button>
          </div>
        </div>
      </header>

      <section className="grid">
        <article className="card">
          <div className="card-head">
            <div>
              <p className="label">{t("connection.title")}</p>
              <p className="muted">{t("connection.subtitle")}</p>
            </div>
            <span className="pill pill-quiet">{t("connection.pill")}</span>
          </div>
          <div className="stack">
            <div className="button-row">
              <button
                id="connectBtn"
                type="button"
                onClick={connectWallet}
                disabled={disableConnect}
              >
                {connecting ? t("compose.sending") : t("connection.connect")}
              </button>
              <button
                id="disconnectBtn"
                type="button"
                className="ghost"
                onClick={() => disconnect(true)}
                disabled={disableDisconnect}
              >
                {t("connection.disconnect")}
              </button>
            </div>
          </div>
          <dl className="meta">
            <div>
              <dt>{t("connection.account")}</dt>
              <dd>{account || "—"}</dd>
            </div>
            <div>
              <dt>{t("connection.chain")}</dt>
              <dd>{chainLabel}</dd>
            </div>
            <div>
              <dt>{t("connection.status")}</dt>
              <dd>{connected ? t("connection.connected") : t("connection.notConnected")}</dd>
            </div>
          </dl>
        </article>

        <article className="card">
          <div className="card-head">
            <div>
              <p className="label">{t("compose.title")}</p>
              <p className="muted">{t("compose.subtitle")}</p>
            </div>
            <span className={chainPillClass}>{chainPillText}</span>
          </div>
          <form
            id="messageForm"
            ref={formRef}
            className="stack"
            noValidate
            onSubmit={handleSendMessage}
          >
            <label className="input-label" htmlFor="toAddress">
              {t("compose.recipientLabel")}{" "}
              <span className="muted">{t("compose.recipientHint")}</span>
            </label>
            <input
              id="toAddress"
              name="to"
              type="text"
              placeholder="0x destination (optional)"
              autoComplete="off"
            />

            <label className="input-label" htmlFor="messageInput">
              {t("compose.messageLabel")}
            </label>
            <textarea
              id="messageInput"
              name="message"
              rows={4}
              maxLength={280}
              placeholder={t("compose.messagePlaceholder")}
              disabled={!connected}
            ></textarea>
            <p className="hint">{t("compose.messageHint")}</p>

            <button id="sendBtn" type="submit" className="primary" disabled={disableSend}>
              {sending ? t("compose.sending") : t("compose.send")}
            </button>
          </form>
        </article>

        <article className="card status-card">
          <div className="card-head">
            <div>
              <p className="label">{t("live.title")}</p>
              <p className="muted">{t("live.subtitle")}</p>
            </div>
          </div>
          <div className={statusClass}>{status.text}</div>
          <div className="status-line secondary">
            <span className="dot"></span>
            <span>{onPolygon ? t("live.chainReady") : t("live.chainRequired")}</span>
          </div>
          <div className="tx-box">
            <div className="label">{t("live.lastTx")}</div>
            <div className="hash">{txHash || "—"}</div>
            <div className="tx-actions">
              <button
                type="button"
                className="ghost copy-btn"
                onClick={handleCopyHash}
                disabled={!hasTxHash}
                title={t("live.copy")}
              >
                {t("live.copy")}
              </button>
              <a
                className={`link ${!hasTxHash ? "disabled" : ""}`}
                href={hasTxHash ? `${polygonscanBase}${txHash}` : undefined}
                target="_blank"
                rel="noreferrer"
                aria-disabled={!hasTxHash}
              >
                {t("live.viewOnPolygonscan")}
              </a>
            </div>
            <div className="hint">{t("live.trackHint")}</div>
          </div>
          <div className="history">
            <div className="history-head">
              <div>
                <p className="label">{t("history.title")}</p>
                <p className="muted">{t("history.subtitle")}</p>
              </div>
            </div>
            {history.length === 0 ? (
              <div className="history-empty">{t("history.empty")}</div>
            ) : (
              <ul className="history-list">
                {history.map((item) => (
                  <li key={item.hash} className="history-item">
                    <div className="history-row">
                      <span className="pill pill-quiet">{t("history.sent")}</span>
                      <span className="history-time">
                        {new Date(item.createdAt).toLocaleTimeString()}
                      </span>
                    </div>
                    <div className="history-meta">
                      <span>
                        {t("history.from")}: {item.from}
                      </span>
                      <span>
                        {t("history.to")}: {item.to}
                      </span>
                    </div>
                    <div className="history-message">{item.message}</div>
                    <div className="history-hash">
                      {item.hash.slice(0, 10)}…{item.hash.slice(-6)}
                    </div>
                  </li>
                ))}
              </ul>
            )}
          </div>
        </article>
      </section>

      <footer className="footer">
        <div className="footer-left">
          <span className="version-pill">1</span>
        </div>
        <a
          className="footer-link"
          href="https://gitea.wittrail.com/wittrail/polynote"
          target="_blank"
          rel="noreferrer"
        >
          <svg className="gitea-icon" viewBox="0 0 64 64" role="img" aria-hidden="true">
            <path
              fill="#61b872"
              d="M8 14.5c0-2 1.6-3.5 3.5-3.5h32c1.9 0 3.5 1.5 3.5 3.5l-1.7 15.4C44.5 38.9 37 45 28.6 45h-8.9c-6.5 0-11.9-4.8-12.7-11.3L8 14.5Z"
            />
            <path fill="#4a7c59" d="M44 18h5.6c3.7 0 6.5 3.4 5.7 7-0.8 3.6-4.1 6-7.8 6H44" />
            <path fill="#9adf72" d="M22.8 19.2c4 1.2 7.7 4.3 8.9 8.6-4.1-1-7.8-4.2-8.9-8.6Z" />
          </svg>
          <span>{t("footer.repo")}</span>
        </a>
      </footer>
    </main>
  );
}

function pickInjectedProvider(): Eip1193Provider | null {
  if (typeof window === "undefined") return null;
  const { ethereum, trustwallet } = window as EthereumWindow;
  if (ethereum?.providers?.length) {
    const trust = ethereum.providers.find((p) => p.isTrustWallet || p.isTrust);
    if (trust) return trust;
    return ethereum.providers[0];
  }
  if (trustwallet) return trustwallet;
  return ethereum || null;
}

function parseChainId(chainId: string | number): number {
  if (typeof chainId === "string" && chainId.startsWith("0x")) {
    return parseInt(chainId, 16);
  }
  return Number(chainId);
}

function normalizeError(
  err: unknown,
  fallback: string,
  t: (key: string, vars?: Record<string, string>) => string
): string {
  if (!err) return fallback;
  if (typeof err === "string") return err;
  if (typeof err === "object") {
    const maybeError = err as { code?: number | string; message?: string; reason?: string };
    if (maybeError.code === 4001 || maybeError.message?.toLowerCase().includes("user rejected"))
      return t("status.rejected");
    if (maybeError.message) return maybeError.message;
    if (maybeError.reason) return maybeError.reason;
  }
  return fallback;
}
