import { useState } from "react";
import { redirect } from "react-router";
import type { Route } from "./+types/auth.register";
import { getSession } from "~/lib/session.server";
import type { RegistrationResponseJSON, AuthenticatorTransportFuture } from "@simplewebauthn/server";

export async function loader({ request }: Route.LoaderArgs) {
  const session = await getSession(request.headers.get("Cookie"));
  if (session.get("authenticated")) return redirect("/");
  return {};
}

function b64urlToBuffer(b64url: string): ArrayBuffer {
  const base64 = b64url.replace(/-/g, "+").replace(/_/g, "/");
  const pad = "=".repeat((4 - (base64.length % 4)) % 4);
  const binary = atob(base64 + pad);
  const bytes = new Uint8Array(binary.length);
  for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
  return bytes.buffer;
}

function bufferToB64url(buf: ArrayBuffer): string {
  const bytes = new Uint8Array(buf);
  let binary = "";
  for (let i = 0; i < bytes.byteLength; i++) binary += String.fromCharCode(bytes[i]);
  return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
}

export default function Register() {
  const [token, setToken] = useState("");
  const [status, setStatus] = useState<"idle" | "loading" | "error">("idle");
  const [errorMsg, setErrorMsg] = useState("");
  const [step, setStep] = useState("");

  async function handleRegister() {
    const tick = () => new Promise<void>((r) => setTimeout(r, 50));
    setStatus("loading");
    setErrorMsg("");
    setStep("");
    try {
      setStep("1a. fetch 中…"); await tick();
      const optRes = await fetch("/api/passkey?intent=reg-options", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ token }),
      });
      setStep("1b. fetch 完了 status=" + optRes.status); await tick();
      const rawText = await optRes.text();
      setStep("1c. body=" + rawText.slice(0, 120)); await tick();
      if (!optRes.ok) {
        throw new Error("HTTP " + optRes.status + ": " + rawText.slice(0, 200));
      }
      let optJSON: Record<string, unknown>;
      try {
        optJSON = JSON.parse(rawText);
      } catch (e) {
        throw new Error("JSON.parse 失敗: " + String(e) + " | body: " + rawText.slice(0, 100));
      }
      setStep("1d. JSON パース OK"); await tick();

      const userObj = optJSON.user as Record<string, string>;
      const challenge = b64urlToBuffer(optJSON.challenge as string);
      setStep("2. credentials.create() 呼び出し中…"); await tick();

      const credential = await navigator.credentials.create({
        publicKey: {
          rp: { name: (optJSON.rp as Record<string, string>).name },
          user: {
            id: b64urlToBuffer(userObj.id),
            name: userObj.name,
            displayName: userObj.displayName ?? userObj.name,
          },
          challenge,
          pubKeyCredParams: optJSON.pubKeyCredParams as PublicKeyCredentialParameters[],
        },
      }) as PublicKeyCredential | null;

      if (!credential) throw new Error("クレデンシャルの作成に失敗しました");
      setStep("3. credential 取得 OK → サーバー送信中…"); await tick();

      const attestation = credential.response as AuthenticatorAttestationResponse;
      const regResponse: RegistrationResponseJSON = {
        id: credential.id,
        rawId: bufferToB64url(credential.rawId),
        response: {
          clientDataJSON: bufferToB64url(attestation.clientDataJSON),
          attestationObject: bufferToB64url(attestation.attestationObject),
          transports: attestation.getTransports
            ? (attestation.getTransports() as AuthenticatorTransportFuture[])
            : [],
        },
        authenticatorAttachment: credential.authenticatorAttachment ?? undefined,
        clientExtensionResults: credential.getClientExtensionResults(),
        type: "public-key",
      };

      const verRes = await fetch("/api/passkey?intent=reg-verify", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(regResponse),
      });
      if (!verRes.ok) {
        const err = await verRes.json();
        throw new Error(err.error ?? "登録に失敗しました");
      }

      window.location.href = "/";
    } catch (e) {
      setStatus("error");
      setErrorMsg(e instanceof Error ? `${e.name}: ${e.message}` : String(e));
    }
  }

  return (
    <div className="wrap">
      <header className="site-header">
        <h1>log</h1>
      </header>
      <div className="auth-box">
        <h2>パスキーを登録 [debug]</h2>
        <p>登録トークンを入力してパスキーを作成します。</p>
        <input
          type="password"
          placeholder="登録トークン"
          value={token}
          onChange={(e) => setToken(e.target.value)}
          onKeyDown={(e) => e.key === "Enter" && handleRegister()}
          style={{
            width: "100%",
            marginBottom: ".75rem",
            padding: ".5rem .75rem",
            border: "1px solid #d1d5db",
            borderRadius: "4px",
            fontSize: ".875rem",
            fontFamily: "ui-monospace, monospace",
          }}
        />
        <button className="btn" onClick={handleRegister} disabled={status === "loading" || !token}>
          {status === "loading" ? "登録中…" : "パスキーを登録"}
        </button>
        {step && (
          <p style={{ marginTop: ".75rem", fontSize: ".75rem", color: "#6b7280", wordBreak: "break-all" }}>{step}</p>
        )}
        {status === "error" && (
          <p className="error-msg" style={{ marginTop: ".5rem" }}>{errorMsg}</p>
        )}
      </div>
    </div>
  );
}
