import * as express from "express";
import { createDecoder, createVerifier } from "fast-jwt";

import HttpError from "../interfaces/http-error";
import HttpStatus from "../interfaces/http-status";
import { handleWebServiceAuth } from "./authWebService";
import { handleInternalAuth } from "./authInternal";

if (!process.env.AUTH_PUBLIC_KEY && !process.env.AUTH_REALM_URL) {
  throw new Error("Require keycloak AUTH_PUBLIC_KEY or AUTH_REALM_URL.");
}
if (process.env.AUTH_PUBLIC_KEY && process.env.AUTH_REALM_URL && !process.env.AUTH_PREFERRED_MODE) {
  throw new Error(
    "AUTH_PREFFERRED must be specified if AUTH_PUBLIC_KEY and AUTH_REALM_URL is provided.",
  );
}

const jwtVerify = createVerifier({
  key: async () => {
    return `-----BEGIN PUBLIC KEY-----\n${process.env.AUTH_PUBLIC_KEY}\n-----END PUBLIC KEY-----`;
  },
});

const jwtDecode = createDecoder();

export async function expressAuthentication(
  request: express.Request,
  securityName: string,
  _scopes?: string[],
) {
  // // API_KEY bypass logic (support api_key, x-api-key, apikey)
  // const apiKeyHeader =
  //   request.headers["api-key"] || request.headers["x-api-key"] || request.headers["apikey"];
  // if (apiKeyHeader !== undefined) {
  //   if (apiKeyHeader === process.env.API_KEY) {
  //     return { preferred_username: "api_key_bypass", apiKeyBypass: true };
  //   }
  // }
  if (process.env.NODE_ENV !== "production" && process.env.AUTH_BYPASS) {
    return { preferred_username: "bypassed" };
  }

  // เพิ่มการจัดการสำหรับ Internal Authentication (.NET service)
  if (securityName === "internalAuth") {
    return await handleInternalAuth(request);
  }

  // เพิ่มการจัดการสำหรับ Web Service Authentication
  if (securityName === "webServiceAuth") {
    return await handleWebServiceAuth(request);
  }

  if (securityName !== "bearerAuth") throw new Error("ไม่ทราบวิธีการยืนยันตัวตน");

  const token = request.headers["authorization"]?.includes("Bearer ")
    ? request.headers["authorization"].split(" ")[1]
    : request.headers["authorization"];

  if (!token) throw new HttpError(HttpStatus.UNAUTHORIZED, "ไม่พบข้อมูลสำหรับยืนยันตัวตน");

  let payload: Record<string, any> = {};

  switch (process.env.AUTH_PREFERRED_MODE) {
    case "online":
      payload = await verifyOnline(token);
      break;
    case "offline":
      payload = await verifyOffline(token);
      break;
    default:
      if (process.env.AUTH_REALM_URL) payload = await verifyOnline(token);
      if (process.env.AUTH_PUBLIC_KEY) payload = await verifyOffline(token);
      break;
  }

  if (!request.app.locals.logData) {
    request.app.locals.logData = {};
  }

  request.app.locals.logData.userId = payload.sub;
  request.app.locals.logData.userName = payload.name;
  request.app.locals.logData.user = payload.preferred_username;

  // เก็บค่า profileId และ orgRootDnaId จาก token (ใช้ค่าว่างถ้าไม่มี)
  request.app.locals.logData.profileId = payload.profileId ?? "";
  request.app.locals.logData.orgRootDnaId = payload.orgRootDnaId ?? "";
  request.app.locals.logData.orgChild1DnaId = payload.orgChild1DnaId ?? "";
  request.app.locals.logData.orgChild2DnaId = payload.orgChild2DnaId ?? "";
  request.app.locals.logData.orgChild3DnaId = payload.orgChild3DnaId ?? "";
  request.app.locals.logData.orgChild4DnaId = payload.orgChild4DnaId ?? "";
  request.app.locals.logData.empType = payload.empType ?? "";
  request.app.locals.logData.prefix = payload.prefix ?? "";

  return payload;
}

async function verifyOffline(token: string) {
  const payload = await jwtVerify(token).catch((_) => null);
  if (!payload) throw new HttpError(HttpStatus.UNAUTHORIZED, "ไม่สามารถยืนยันตัวตนได้");
  return payload;
}

async function verifyOnline(token: string) {
  const res = await fetch(`${process.env.AUTH_REALM_URL}/protocol/openid-connect/userinfo`, {
    headers: { authorization: `Bearer ${token}` },
  }).catch((e) => console.error(e));

  if (!res) throw new Error("ไม่สามารถเข้าถึงระบบยืนยันตัวตน");
  if (!res.ok) throw new HttpError(HttpStatus.UNAUTHORIZED, "ไม่สามารถยืนยันตัวตนได้");

  return await jwtDecode(token);
}
