import { createFileRoute } from "@tanstack/react-router";
import { useEffect, useMemo, useState } from "react";
import { Copy, RefreshCw, ShieldCheck } from "lucide-react";
import { ToolShell } from "@/components/ToolShell";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label";

export const Route = createFileRoute("/tools/totp-authenticator")({
  head: () => ({
    meta: [
      { title: "2FA Authenticator — Generate TOTP codes in your browser" },
      {
        name: "description",
        content:
          "Generate time-based one-time passwords (TOTP / 2FA codes) from a Base32 secret. 100% client-side, no data leaves your browser.",
      },
      { property: "og:title", content: "2FA Authenticator — TOTP code generator" },
      {
        property: "og:description",
        content: "Generate RFC 6238 TOTP codes from a Base32 secret. Private, in-browser.",
      },
    ],
  }),
  component: TotpAuthenticator,
});

// --- Base32 decode (RFC 4648) ---
function base32Decode(input: string): Uint8Array {
  const cleaned = input.replace(/\s+/g, "").replace(/=+$/, "").toUpperCase();
  const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
  let bits = 0;
  let value = 0;
  const out: number[] = [];
  for (const ch of cleaned) {
    const idx = alphabet.indexOf(ch);
    if (idx === -1) throw new Error(`Invalid Base32 character: "${ch}"`);
    value = (value << 5) | idx;
    bits += 5;
    if (bits >= 8) {
      bits -= 8;
      out.push((value >>> bits) & 0xff);
    }
  }
  return new Uint8Array(out);
}

async function generateTOTP(secret: string, period = 30, digits = 6): Promise<string> {
  const key = base32Decode(secret);
  const counter = Math.floor(Date.now() / 1000 / period);
  const counterBytes = new ArrayBuffer(8);
  const view = new DataView(counterBytes);
  view.setUint32(4, counter, false);

  const cryptoKey = await crypto.subtle.importKey(
    "raw",
    key as BufferSource,
    { name: "HMAC", hash: "SHA-1" },
    false,
    ["sign"],
  );
  const sig = new Uint8Array(await crypto.subtle.sign("HMAC", cryptoKey, counterBytes));
  const offset = sig[sig.length - 1] & 0x0f;
  const bin =
    ((sig[offset] & 0x7f) << 24) |
    ((sig[offset + 1] & 0xff) << 16) |
    ((sig[offset + 2] & 0xff) << 8) |
    (sig[offset + 3] & 0xff);
  const code = (bin % 10 ** digits).toString().padStart(digits, "0");
  return code;
}

function TotpAuthenticator() {
  const [secret, setSecret] = useState("JBSWY3DPEHPK3PXP"); // demo: "Hello!\xDE\xAD\xBE\xEF"
  const [code, setCode] = useState("------");
  const [error, setError] = useState<string | null>(null);
  const [now, setNow] = useState(() => Math.floor(Date.now() / 1000));

  const period = 30;
  const remaining = period - (now % period);
  const progress = useMemo(() => (remaining / period) * 100, [remaining]);

  useEffect(() => {
    const tick = () => setNow(Math.floor(Date.now() / 1000));
    tick();
    const id = setInterval(tick, 1000);
    return () => clearInterval(id);
  }, []);

  useEffect(() => {
    let cancelled = false;
    if (!secret.trim()) {
      setCode("------");
      setError(null);
      return;
    }
    generateTOTP(secret.trim(), period, 6)
      .then((c) => {
        if (!cancelled) {
          setCode(c);
          setError(null);
        }
      })
      .catch((e: Error) => {
        if (!cancelled) {
          setCode("------");
          setError(e.message);
        }
      });
    return () => {
      cancelled = true;
    };
  }, [secret, now]);

  const generateRandomSecret = () => {
    const bytes = new Uint8Array(20);
    crypto.getRandomValues(bytes);
    const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
    let bits = 0;
    let value = 0;
    let out = "";
    for (const b of bytes) {
      value = (value << 8) | b;
      bits += 8;
      while (bits >= 5) {
        bits -= 5;
        out += alphabet[(value >>> bits) & 0x1f];
      }
    }
    setSecret(out);
  };

  const copyCode = () => {
    if (!error) navigator.clipboard.writeText(code);
  };

  return (
    <ToolShell
      title="2FA Authenticator"
      description="Generate time-based one-time passwords (TOTP) from a Base32 secret. Compatible with Google Authenticator, Authy, 1Password and any RFC 6238 service. Runs entirely in your browser."
    >
      <div className="space-y-6">
        <div className="space-y-2">
          <Label htmlFor="secret">Base32 secret</Label>
          <div className="flex gap-2">
            <Input
              id="secret"
              value={secret}
              onChange={(e) => setSecret(e.target.value)}
              placeholder="e.g. JBSWY3DPEHPK3PXP"
              className="font-mono"
              autoComplete="off"
              spellCheck={false}
            />
            <Button variant="outline" onClick={generateRandomSecret} title="Generate random secret">
              <RefreshCw className="h-4 w-4" />
            </Button>
          </div>
          {error && <p className="text-sm text-destructive">{error}</p>}
        </div>

        <div
          className="overflow-hidden rounded-2xl border border-border/80 p-8 text-center"
          style={{ background: "var(--gradient-subtle)" }}
        >
          <div className="flex items-center justify-center gap-2 text-xs uppercase tracking-wider text-muted-foreground">
            <ShieldCheck className="h-3.5 w-3.5 text-primary" />
            Current code
          </div>
          <div
            className="mt-4 select-all bg-clip-text font-mono text-6xl font-semibold tracking-[0.25em] text-transparent tabular-nums sm:text-7xl"
            style={{ backgroundImage: "var(--gradient-primary)" }}
          >
            {code.replace(/(\d{3})(\d{3})/, "$1 $2")}
          </div>

          <div className="mx-auto mt-6 max-w-xs">
            <div className="h-1.5 w-full overflow-hidden rounded-full bg-border">
              <div
                className="h-full rounded-full transition-[width] duration-1000 ease-linear"
                style={{
                  width: `${progress}%`,
                  background: "var(--gradient-primary)",
                }}
              />
            </div>
            <p className="mt-2 text-xs text-muted-foreground">
              Refreshes in <span className="font-medium text-foreground">{remaining}s</span>
            </p>
          </div>

          <Button onClick={copyCode} variant="outline" size="sm" className="mt-6" disabled={!!error}>
            <Copy className="h-3.5 w-3.5" /> Copy code
          </Button>
        </div>

        <div className="rounded-xl border border-border/60 bg-muted/30 p-4 text-sm text-muted-foreground">
          <p>
            <strong className="text-foreground">How it works:</strong> Paste the Base32 secret your
            service shows when enabling 2FA (often hidden behind "Can't scan QR?"). The code is
            computed locally using HMAC-SHA1 per RFC 6238 — nothing is sent to any server.
          </p>
        </div>
      </div>
    </ToolShell>
  );
}
