/**
 * POST /api/auth/login — 共有トークンを検証して `dwatch_session` cookie を発行する。
 *
 * ★このルートは認証をかけない★（`lib/proxy/authenticate.ts` の `isPublicPath()` で通す）。
 *   保護すると誰もログインできない。IP制限は proxy が別途かけるので、
 *   許可されないIPからはここに到達する前に 403 になる。
 *
 * ★入力値（トークン）をログにも本文にも絶対に出さないこと★
 * ★失敗しても「トークンが違う」以上の情報を返さないこと★
 *   （「そのトークンは存在するが期限切れ」等を返すと総当たりの手がかりになる）
 */
import { NextResponse } from "next/server";
import { isJsonObject } from "@/lib/api/json-body";
import {
  SESSION_COOKIE_NAME,
  SESSION_COOKIE_OPTIONS,
} from "@/lib/auth/cookie";
import { readAuthToken, verifyToken } from "@/lib/auth/token";
import { logEvent, withRequestId } from "@/lib/logger";
import type { ApiError, ErrorCode } from "@/lib/types";

const ROUTE = "/api/auth/login";

function errorResponse(status: number, code: ErrorCode, message: string) {
  const body: ApiError = { error: { code, message, details: null } };
  return NextResponse.json(body, { status });
}

export async function POST(request: Request) {
  return withRequestId(request, async () => {
    let body: unknown;
    try {
      body = await request.json();
    } catch {
      return errorResponse(422, "validation_failed", "JSONの形式が不正です");
    }
    // ★本文がオブジェクトでない（`null`・配列・数値・文字列）なら422★（指示書BB A5・D19。今の「JSON が読めない」と同じ文言）
    if (!isJsonObject(body)) {
      return errorResponse(422, "validation_failed", "JSONの形式が不正です");
    }

    const token = body.token;
    if (typeof token !== "string" || token.length === 0) {
      return errorResponse(422, "validation_failed", "tokenを指定してください");
    }

    // ★定数時間比較。ここで === を書かないこと★
    if (!verifyToken(token, readAuthToken())) {
      // ★入力されたトークンは載せない（成否だけ）★
      // ★client_ip も載せない★ — ここに来た時点でIP判定は通過済みで、
      //   接続元は定義上 ALLOWED_IPS に載っている既知の値（D-101）
      logEvent("warn", {
        event: "auth.login_failed",
        route: ROUTE,
        method: "POST",
      });
      return errorResponse(401, "invalid_token", "トークンが違います");
    }

    logEvent("info", {
      event: "auth.login_success",
      route: ROUTE,
      method: "POST",
    });

    // 本文なしの 204。cookie は Set-Cookie で返す
    const response = new NextResponse(null, { status: 204 });
    // ★expires / maxAge は付けない（CLAUDE.md §7 でトークン期限は禁止）★
    response.cookies.set({
      name: SESSION_COOKIE_NAME,
      value: token,
      ...SESSION_COOKIE_OPTIONS,
    });
    return response;
  });
}
