import type { NextFunction, Request, Response } from "express";

import { config } from "../config.js";
import { logger } from "../logger.js";

export type AgentAuthContext = {
  accessToken: string;
  userId: string;
  keycloakSub: string;
  username: string;
  role: string;
  isSuperuser: boolean;
  projectId: string;
  network: string;
  projectRole: string;
  tokenExpiresAt?: string;
};

type AgentAuthContextResponse = {
  user_id?: unknown;
  keycloak_sub?: unknown;
  username?: unknown;
  role?: unknown;
  is_superuser?: unknown;
  project_id?: unknown;
  network?: unknown;
  project_role?: unknown;
  token_expires_at?: unknown;
};

declare global {
  namespace Express {
    interface Request {
      agentAuth?: AgentAuthContext;
    }
  }
}

export const extractBearerToken = (authorization?: string) => {
  const value = authorization?.trim();
  if (!value) {
    return "";
  }
  return value.replace(/^Bearer\s+/i, "").trim();
};

const normalizeAgentAuthContext = (
  payload: AgentAuthContextResponse,
  accessToken: string,
): AgentAuthContext | null => {
  if (
    typeof payload.user_id !== "string" ||
    typeof payload.keycloak_sub !== "string" ||
    typeof payload.username !== "string" ||
    typeof payload.role !== "string" ||
    typeof payload.is_superuser !== "boolean" ||
    typeof payload.project_id !== "string" ||
    typeof payload.network !== "string" ||
    typeof payload.project_role !== "string"
  ) {
    return null;
  }
  return {
    accessToken,
    userId: payload.user_id,
    keycloakSub: payload.keycloak_sub,
    username: payload.username,
    role: payload.role,
    isSuperuser: payload.is_superuser,
    projectId: payload.project_id,
    network: payload.network,
    projectRole: payload.project_role,
    tokenExpiresAt:
      typeof payload.token_expires_at === "string"
        ? payload.token_expires_at
        : undefined,
  };
};

// Agent API 使用 TJWater 后端专用认证上下文：后端负责校验 Keycloak token、
// metadata 用户状态和项目 membership，Agent 只信任后端返回的 user/project。
export const requireAgentAuth = async (
  req: Request,
  res: Response,
  next: NextFunction,
) => {
  const token = extractBearerToken(req.header("authorization"));
  if (!token) {
    res.status(401).json({ message: "authorization token is required" });
    return;
  }

  const projectId = req.header("x-project-id")?.trim();
  if (!projectId) {
    res.status(400).json({ message: "x-project-id is required" });
    return;
  }

  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), config.AGENT_AUTH_TIMEOUT_MS);

  try {
    const response = await fetch(new URL("/api/v1/agent-auth-context", config.TJWATER_API_BASE_URL), {
      method: "GET",
      headers: {
        Accept: "application/json",
        Authorization: `Bearer ${token}`,
        "X-Project-Id": projectId,
        ...(req.header("x-trace-id") ? { "X-Trace-Id": req.header("x-trace-id") as string } : {}),
      },
      signal: controller.signal,
    });

    if (response.ok) {
      const context = normalizeAgentAuthContext(
        (await response.json()) as AgentAuthContextResponse,
        token,
      );
      if (!context) {
        res.status(502).json({ message: "invalid authentication context" });
        return;
      }
      req.agentAuth = context;
      next();
      return;
    }

    const status =
      response.status === 400 ||
      response.status === 401 ||
      response.status === 403 ||
      response.status === 404
        ? response.status
        : response.status === 503
          ? 503
          : 502;
    const messages: Record<number, string> = {
      400: "invalid authentication request",
      401: "unauthorized",
      403: "forbidden",
      404: "authentication context not found",
      502: "invalid authentication service response",
      503: "authentication service unavailable",
    };
    res.status(status).json({ message: messages[status] });
  } catch (error) {
    logger.warn({ err: error }, "agent auth validation failed");
    res.status(503).json({ message: "authentication service unavailable" });
  } finally {
    clearTimeout(timer);
  }
};

export const getAgentAuthContext = (req: Request) => {
  if (!req.agentAuth) {
    throw new Error("agent auth context is missing");
  }
  return req.agentAuth;
};
