import { useState } from "react";
import { redirect } from "react-router";
import {
  generateRegistrationOptions,
  verifyRegistrationResponse,
  type RegistrationResponseJSON,
  type AuthenticatorTransportFuture,
} from "@simplewebauthn/server";
import type { Route } from "./+types/auth.register";
import { getSession, commitSession } from "~/lib/session.server";
import { saveCredential } from "~/lib/db.server";
import { rpID, rpName, origin } from "~/lib/passkey.server";

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

export async function action({ request }: Route.ActionArgs) {
  const url = new URL(request.url);
  const intent = url.searchParams.get("intent");
  const session = await getSession(request.headers.get("Cookie"));

  if (intent === "options") {
    const body = await request.json() as { token?: string };
    const registerToken = process.env.REGISTER_TOKEN;
    if (!registerToken || body.token !== registerToken) {
      return Response.json({ error: "トークンが違います" }, { status: 403 });
    }

    const options = await generateRegistrationOptions({
      rpName,
      rpID,
      userName: "admin",
      attestationType: "none",
      authenticatorSelection: {
        residentKey: "required",
        userVerification: "required",
      },
    });
    session.set("challenge", options.challenge);
    return Response.json(options, {
      headers: { "Set-Cookie": await commitSession(session) },
    });
  }

  if (intent === "verify") {
    const challenge = session.get("challenge") as string | undefined;
    if (!challenge) return Response.json({ error: "セッション切れです" }, { status: 400 });

    const body = (await request.json()) as RegistrationResponseJSON;
    const verification = await verifyRegistrationResponse({
      response: body,
      expectedChallenge: challenge,
      expectedOrigin: origin,
      expectedRPID: rpID,
    });

    if (!verification.verified || !verification.registrationInfo) {
      return Response.json({ error: "登録に失敗しました" }, { status: 400 });
    }

    const { credential } = verification.registrationInfo;
    saveCredential({
      id: credential.id,
      publicKey: credential.publicKey,
      counter: credential.counter,
      transports: (body.response as { transports?: AuthenticatorTransportFuture[] }).transports,
    });

    session.unset("challenge");
    session.set("authenticated", true);
    return redirect("/", {
      headers: { "Set-Cookie": await commitSession(session) },
    });
  }

  return Response.json({ error: "不正なリクエスト" }, { status: 400 });
}

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

  async function handleRegister() {
    setStatus("loading");
    setErrorMsg("");
    try {
      const { startRegistration } = await import("@simplewebauthn/browser");

      const optRes = await fetch("/auth/register?intent=options", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ token }),
      });
      if (!optRes.ok) {
        const err = await optRes.json();
        throw new Error(err.error ?? "オプション取得に失敗しました");
      }
      const options = await optRes.json();

      const regResponse = await startRegistration({ optionsJSON: options });

      const verRes = await fetch("/auth/register?intent=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.message : "エラーが発生しました");
    }
  }

  return (
    <div className="wrap">
      <header className="site-header">
        <h1>log</h1>
      </header>
      <div className="auth-box">
        <h2>パスキーを登録</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>
        {status === "error" && (
          <p className="error-msg" style={{ marginTop: ".75rem" }}>{errorMsg}</p>
        )}
      </div>
    </div>
  );
}
