import express from "express";
import { createServer as createHttpServer } from "node:http";
import { randomBytes, randomUUID, createHash, timingSafeEqual } from "node:crypto";
import { existsSync, readFileSync } from "node:fs";
import { mkdir, writeFile } from "node:fs/promises";
import { dirname, extname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { createServer as createViteServer } from "vite";
import { createRemoteJWKSet, jwtVerify } from "jose";
import { createAuthentikSyncClient, resolveRequiredGroups } from "./authentik-sync.mjs";
import { createControlPlaneStore } from "./control-plane-store.mjs";

const serverRoot = dirname(fileURLToPath(import.meta.url));
const projectRoot = resolve(serverRoot, "..");
const maxStorageJsonBodyBytes = "260mb";
const pendingLoginTtlMs = 10 * 60 * 1000;
const sessionTtlMs = 12 * 60 * 60 * 1000;
const oidcStateCookieName = "nodedc_oidc_state";
const maxOidcStateCookieEntries = 8;
const sessionCookieName = "nodedc_session";

loadEnvFiles([
  process.env.NODEDC_PLATFORM_ENV,
  resolve(projectRoot, ".env"),
  resolve(projectRoot, "..", "..", "NODEDC", "platform", "infra", ".env"),
]);

const config = readConfig();
const app = express();
const httpServer = createHttpServer(app);
const controlPlaneStore = createControlPlaneStore({ projectRoot });
const authentikSyncClient = createAuthentikSyncClient({ baseUrl: config.authentikBaseUrl, token: config.authentikApiToken });
const pendingLogins = new Map();
const sessions = new Map();
const runtimeEventClients = new Set();
let discoveryCache = null;
let jwksCache = null;

app.disable("x-powered-by");
app.use(express.json({ limit: maxStorageJsonBodyBytes }));

app.get("/healthz", (_req, res) => {
  res.json({
    ok: true,
    service: "nodedc-launcher-bff",
    oidcConfigured: config.oidcConfigured,
    authentikApiConfigured: authentikSyncClient.isConfigured(),
    internalAccessApiConfigured: Boolean(config.internalAccessToken),
  });
});

app.get("/auth/login", asyncRoute(async (req, res) => {
  ensureOidcConfigured();

  const discovery = await getOidcDiscovery();
  const state = randomBase64Url(32);
  const nonce = randomBase64Url(32);
  const codeVerifier = randomBase64Url(64);
  const codeChallenge = createHash("sha256").update(codeVerifier).digest("base64url");
  const returnTo = sanitizeReturnTo(req.query.returnTo);

  pruneExpiredState();
  pendingLogins.set(state, {
    codeVerifier,
    nonce,
    returnTo,
    expiresAt: Date.now() + pendingLoginTtlMs,
  });

  setOidcStateCookie(res, [state, ...getValidOidcCookieStates(req)].slice(0, maxOidcStateCookieEntries));

  const authorizationUrl = new URL(discovery.authorization_endpoint);
  authorizationUrl.searchParams.set("response_type", "code");
  authorizationUrl.searchParams.set("client_id", config.clientId);
  authorizationUrl.searchParams.set("redirect_uri", config.redirectUri);
  authorizationUrl.searchParams.set("scope", config.scope);
  authorizationUrl.searchParams.set("state", state);
  authorizationUrl.searchParams.set("nonce", nonce);
  authorizationUrl.searchParams.set("code_challenge", codeChallenge);
  authorizationUrl.searchParams.set("code_challenge_method", "S256");

  const prompt = sanitizePrompt(req.query.prompt);

  if (prompt) {
    authorizationUrl.searchParams.set("prompt", prompt);
  }

  res.redirect(authorizationUrl.toString());
}));

app.get("/auth/callback", asyncRoute(async (req, res) => {
  ensureOidcConfigured();

  const error = typeof req.query.error === "string" ? req.query.error : null;
  if (error) {
    throw new Error(`OIDC provider returned error: ${error}`);
  }

  const code = typeof req.query.code === "string" ? req.query.code : null;
  const state = typeof req.query.state === "string" ? req.query.state : null;
  const cookieStates = getValidOidcCookieStates(req);

  if (!code || !state || !cookieStates.includes(state)) {
    res.clearCookie(oidcStateCookieName, clearCookieOptions());
    res.redirect("/auth/login?returnTo=/");
    return;
  }

  const pendingLogin = pendingLogins.get(state);
  pendingLogins.delete(state);
  setOidcStateCookie(res, cookieStates.filter((cookieState) => cookieState !== state));

  if (!pendingLogin || pendingLogin.expiresAt < Date.now()) {
    throw new Error("OIDC login state expired");
  }

  const discovery = await getOidcDiscovery();
  const tokenSet = await exchangeCodeForTokens(discovery, code, pendingLogin.codeVerifier);
  const claims = await verifyIdToken(discovery, tokenSet.id_token, pendingLogin.nonce);
  const sessionId = randomBase64Url(48);
  const session = {
    id: sessionId,
    user: normalizeUser(claims),
    tokenSet: {
      idToken: tokenSet.id_token,
      accessToken: tokenSet.access_token ?? null,
      expiresAt: tokenSet.expires_in ? Date.now() + Number(tokenSet.expires_in) * 1000 : null,
    },
    createdAt: Date.now(),
    expiresAt: Date.now() + sessionTtlMs,
  };

  pruneExpiredSessions();
  sessions.set(sessionId, session);
  res.cookie(sessionCookieName, sessionId, cookieOptions(sessionTtlMs));
  res.redirect(pendingLogin.returnTo);
}));

app.get("/auth/logged-out", (req, res) => {
  const returnTo = sanitizeReturnTo(req.query.returnTo);

  res.clearCookie(sessionCookieName, clearCookieOptions());
  res.clearCookie(oidcStateCookieName, clearCookieOptions());
  setNoStore(res);
  res.redirect(buildLoginRedirectUrl(returnTo, { forceLogin: true }));
});

app.get("/auth/session-sync", (req, res) => {
  const allowedOrigins = getSessionSyncAllowedOrigins();
  setNoStore(res);
  res.setHeader(
    "Content-Security-Policy",
    `default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; frame-ancestors ${allowedOrigins.join(" ")}`
  );
  res.type("html").send(renderSessionSyncBridgePage(allowedOrigins));
});

app.get("/auth/logout", asyncRoute(async (req, res) => {
  const session = getCurrentSession(req);
  const returnTo = sanitizeReturnTo(req.query.returnTo);
  const globalLogout = req.query.global === "1" || req.query.global === "true";

  if (session) {
    sessions.delete(session.id);
  }

  res.clearCookie(sessionCookieName, clearCookieOptions());

  if (!globalLogout || !config.oidcConfigured) {
    setNoStore(res);
    res.redirect(returnTo);
    return;
  }

  const discovery = await getOidcDiscovery();
  const endSessionEndpoint = discovery.end_session_endpoint;
  const loginRedirectUrl = buildLoginRedirectUrl(returnTo, { forceLogin: true });
  const postLogoutRedirectUrl = buildLoggedOutRedirectUrl();

  if (!endSessionEndpoint) {
    setNoStore(res);
    res.type("html").send(renderGlobalLogoutPage(getFrontchannelLogoutUrls(), loginRedirectUrl));
    return;
  }

  const logoutUrl = new URL(endSessionEndpoint);
  logoutUrl.searchParams.set("client_id", config.clientId);
  logoutUrl.searchParams.set("post_logout_redirect_uri", postLogoutRedirectUrl);

  if (session?.tokenSet.idToken) {
    logoutUrl.searchParams.set("id_token_hint", session.tokenSet.idToken);
  }

  setNoStore(res);
  res.type("html").send(renderGlobalLogoutPage(getFrontchannelLogoutUrls(), logoutUrl.toString()));
}));

app.get("/api/me", (req, res) => {
  const session = getCurrentSession(req);

  if (!session) {
    res.status(401).json({ authenticated: false, loginUrl: "/auth/login" });
    return;
  }

  const runtimeContext = getRuntimeSessionContext(session);

  res.json({
    authenticated: true,
    user: runtimeContext.user,
    groups: runtimeContext.groups,
    isSuperAdmin: runtimeContext.groups.includes("nodedc:superadmin"),
    logoutUrl: "/auth/logout?global=1&returnTo=/",
  });
});

app.get("/api/apps", (req, res) => {
  const session = getCurrentSession(req);

  if (!session) {
    res.status(401).json({ authenticated: false, loginUrl: "/auth/login" });
    return;
  }

  res.json({ apps: getAppsForSession(session) });
});

app.get("/api/profile", requireSession, (req, res) => {
  const { actor, data } = getLauncherProfileContext(req.nodedcSession);
  const user = findLauncherUser(data, actor.id);

  res.json({
    user,
    memberships: data.memberships.filter((membership) => membership.userId === user.id),
  });
});

app.get("/api/events", requireSession, (req, res) => {
  const client = {
    id: randomUUID(),
    res,
  };

  res.setHeader("Content-Type", "text/event-stream");
  res.setHeader("Cache-Control", "no-cache, no-transform");
  res.setHeader("Connection", "keep-alive");
  res.setHeader("X-Accel-Buffering", "no");
  res.flushHeaders?.();
  res.write(`event: nodedc-ready\ndata: ${JSON.stringify({ ok: true })}\n\n`);

  const keepAlive = setInterval(() => {
    res.write(": keep-alive\n\n");
  }, 30000);

  runtimeEventClients.add(client);

  req.on("close", () => {
    clearInterval(keepAlive);
    runtimeEventClients.delete(client);
  });
});

app.post("/api/internal/access/check", (req, res) => {
  if (!isInternalRequestAuthorized(req)) {
    res.status(config.internalAccessToken ? 401 : 503).json({
      ok: false,
      error: config.internalAccessToken ? "internal_access_unauthorized" : "internal_access_not_configured",
    });
    return;
  }

  const snapshot = controlPlaneStore.getSnapshot({ name: "NODE.DC internal access check" });
  const user = findInternalAccessUser(snapshot.data, req.body);
  const serviceSlug = sanitizeServiceSlug(req.body?.serviceSlug);

  if (!user) {
    res.json({
      ok: true,
      allowed: false,
      reason: "user_not_found",
      serviceSlug,
      groups: [],
      matchedGroups: [],
      user: null,
    });
    return;
  }

  const groups = resolveRequiredGroups(snapshot.data, user);
  const app = getAppsForUser(groups).find((candidate) => candidate.slug === serviceSlug);
  const allowed = Boolean(app?.hasAccess);

  res.json({
    ok: true,
    allowed,
    reason: allowed ? "access_confirmed" : "access_denied",
    serviceSlug,
    groups,
    matchedGroups: app?.matchedGroups ?? [],
    user: {
      id: user.id,
      email: user.email,
      name: user.name,
      avatarUrl: user.avatarUrl ?? null,
      authentikUserId: user.authentikUserId ?? null,
      globalStatus: user.globalStatus,
    },
  });
});

app.patch("/api/profile", requireSession, asyncRoute(async (req, res) => {
  const { actor } = getLauncherProfileContext(req.nodedcSession);
  const result = await controlPlaneStore.updateUserProfile(actor.id, sanitizeSelfProfilePatch(req.body), req.nodedcSession.user);
  const provisionedUser = await authentikSyncClient.provisionUser({
    data: result.data,
    userId: actor.id,
  });
  const storeResult = await controlPlaneStore.markUserAuthentikProvisioned(actor.id, provisionedUser, req.nodedcSession.user);

  publishControlPlaneEvent("profile.updated", [actor.id]);
  res.json({ ...storeResult, provisioning: toProvisioningResponse(provisionedUser) });
}));

app.post("/api/profile/password", requireSession, asyncRoute(async (req, res) => {
  const newPassword = sanitizeNewPassword(req.body?.newPassword);
  const { actor, data } = getLauncherProfileContext(req.nodedcSession);
  const provisionedUser = await authentikSyncClient.provisionUser({
    data,
    userId: actor.id,
    password: newPassword,
  });
  const result = await controlPlaneStore.markUserAuthentikProvisioned(actor.id, provisionedUser, req.nodedcSession.user);

  publishControlPlaneEvent("profile.password.updated", [actor.id]);
  res.json({ data: result.data, ok: true });
}));

app.get("/api/admin/control-plane", requireLauncherAdmin, (req, res) => {
  res.json(controlPlaneStore.getSnapshot(req.nodedcSession.user));
});

app.get("/api/admin/clients", requireLauncherAdmin, (req, res) => {
  const snapshot = controlPlaneStore.getSnapshot(req.nodedcSession.user);
  res.json({ clients: snapshot.data.clients });
});

app.post("/api/admin/clients", requireLauncherAdmin, asyncRoute(async (req, res) => {
  const result = await controlPlaneStore.createClient(req.body, req.nodedcSession.user);
  res.status(201).json(result);
}));

app.patch("/api/admin/clients/:clientId", requireLauncherAdmin, asyncRoute(async (req, res) => {
  const result = await controlPlaneStore.updateClient(req.params.clientId, req.body, req.nodedcSession.user);
  res.json(result);
}));

app.delete("/api/admin/clients/:clientId", requireLauncherAdmin, asyncRoute(async (req, res) => {
  const result = await controlPlaneStore.deleteClient(req.params.clientId, req.nodedcSession.user);
  res.json(result);
}));

app.get("/api/admin/users", requireLauncherAdmin, (req, res) => {
  const snapshot = controlPlaneStore.getSnapshot(req.nodedcSession.user);
  res.json({ users: snapshot.data.users, memberships: snapshot.data.memberships });
});

app.post("/api/admin/users", requireLauncherAdmin, asyncRoute(async (req, res) => {
  const result = await controlPlaneStore.createUser(req.body, req.nodedcSession.user);
  let provisioning = null;

  if (req.body?.provisionAuth !== false) {
    const provisionedUser = await authentikSyncClient.provisionUser({
      data: result.data,
      userId: result.user.id,
      password: sanitizePassword(req.body?.password),
      generatePassword: req.body?.generatePassword !== false,
    });
    const storeResult = await controlPlaneStore.markUserAuthentikProvisioned(result.user.id, provisionedUser, req.nodedcSession.user);
    result.data = storeResult.data;
    provisioning = toProvisioningResponse(provisionedUser);
  }

  publishControlPlaneEvent("admin.user.created", [result.user.id]);
  res.status(201).json({ ...result, provisioning });
}));

app.patch("/api/admin/users/:userId/profile", requireLauncherAdmin, asyncRoute(async (req, res) => {
  const result = await controlPlaneStore.updateUserProfile(req.params.userId, req.body, req.nodedcSession.user);
  const syncResult = await syncUsersToAuthentik(result.data, [req.params.userId], req.nodedcSession.user);
  publishControlPlaneEvent("admin.user.updated", syncResult.userIds);
  res.json({ ...result, data: syncResult.data });
}));

app.post("/api/admin/users/:userId/provision-authentik", requireLauncherAdmin, asyncRoute(async (req, res) => {
  const snapshot = controlPlaneStore.getSnapshot(req.nodedcSession.user);
  const provisionedUser = await authentikSyncClient.provisionUser({
    data: snapshot.data,
    userId: req.params.userId,
    password: sanitizePassword(req.body?.password),
    generatePassword: req.body?.generatePassword === true,
  });
  const result = await controlPlaneStore.markUserAuthentikProvisioned(req.params.userId, provisionedUser, req.nodedcSession.user);

  publishControlPlaneEvent("admin.user.provisioned", [req.params.userId]);
  res.json({ ...result, provisioning: toProvisioningResponse(provisionedUser) });
}));

app.patch("/api/admin/memberships/:membershipId", requireLauncherAdmin, asyncRoute(async (req, res) => {
  const result = await controlPlaneStore.updateMembership(req.params.membershipId, req.body, req.nodedcSession.user);
  const syncResult = await syncUsersToAuthentik(result.data, [result.membership.userId], req.nodedcSession.user);
  publishControlPlaneEvent("admin.membership.updated", syncResult.userIds);
  res.json({ ...result, data: syncResult.data });
}));

app.delete("/api/admin/memberships/:membershipId", requireLauncherAdmin, asyncRoute(async (req, res) => {
  const result = await controlPlaneStore.deleteMembership(req.params.membershipId, req.nodedcSession.user);
  const syncResult = await syncUsersToAuthentik(result.data, [result.membership.userId], req.nodedcSession.user);
  publishControlPlaneEvent("admin.membership.deleted", syncResult.userIds);
  res.json({ ...result, data: syncResult.data });
}));

app.post("/api/admin/invites", requireLauncherAdmin, asyncRoute(async (req, res) => {
  const result = await controlPlaneStore.createInvite(req.body, req.nodedcSession.user);
  publishControlPlaneEvent("admin.invite.created");
  res.status(201).json(result);
}));

app.patch("/api/admin/invites/:inviteId", requireLauncherAdmin, asyncRoute(async (req, res) => {
  const result = await controlPlaneStore.updateInvite(req.params.inviteId, req.body, req.nodedcSession.user);
  publishControlPlaneEvent("admin.invite.updated");
  res.json(result);
}));

app.delete("/api/admin/invites/:inviteId", requireLauncherAdmin, asyncRoute(async (req, res) => {
  const result = await controlPlaneStore.deleteInvite(req.params.inviteId, req.nodedcSession.user);
  publishControlPlaneEvent("admin.invite.deleted");
  res.json(result);
}));

app.post("/api/admin/groups", requireLauncherAdmin, asyncRoute(async (req, res) => {
  const result = await controlPlaneStore.createGroup(req.body, req.nodedcSession.user);
  const syncResult = await syncUsersToAuthentik(result.data, result.group.memberIds, req.nodedcSession.user);
  publishControlPlaneEvent("admin.group.created", syncResult.userIds);
  res.status(201).json({ ...result, data: syncResult.data });
}));

app.patch("/api/admin/groups/:groupId", requireLauncherAdmin, asyncRoute(async (req, res) => {
  const beforeSnapshot = controlPlaneStore.getSnapshot(req.nodedcSession.user);
  const previousMemberIds = beforeSnapshot.data.groups.find((group) => group.id === req.params.groupId)?.memberIds ?? [];
  const result = await controlPlaneStore.updateGroup(req.params.groupId, req.body, req.nodedcSession.user);
  const syncResult = await syncUsersToAuthentik(
    result.data,
    [...previousMemberIds, ...result.group.memberIds],
    req.nodedcSession.user
  );
  publishControlPlaneEvent("admin.group.updated", syncResult.userIds);
  res.json({ ...result, data: syncResult.data });
}));

app.delete("/api/admin/groups/:groupId", requireLauncherAdmin, asyncRoute(async (req, res) => {
  const result = await controlPlaneStore.deleteGroup(req.params.groupId, req.nodedcSession.user);
  const syncResult = await syncUsersToAuthentik(result.data, result.group.memberIds, req.nodedcSession.user);
  publishControlPlaneEvent("admin.group.deleted", syncResult.userIds);
  res.json({ ...result, data: syncResult.data });
}));

app.post("/api/admin/services", requireLauncherAdmin, asyncRoute(async (req, res) => {
  const result = await controlPlaneStore.createService(req.body, req.nodedcSession.user);
  publishControlPlaneEvent("admin.service.created");
  res.status(201).json(result);
}));

app.patch("/api/admin/services/reorder", requireLauncherAdmin, asyncRoute(async (req, res) => {
  const result = await controlPlaneStore.reorderServices(req.body, req.nodedcSession.user);
  publishControlPlaneEvent("admin.service.reordered");
  res.json(result);
}));

app.patch("/api/admin/services/:serviceId", requireLauncherAdmin, asyncRoute(async (req, res) => {
  const result = await controlPlaneStore.updateService(req.params.serviceId, req.body, req.nodedcSession.user);
  publishControlPlaneEvent("admin.service.updated");
  res.json(result);
}));

app.delete("/api/admin/services/:serviceId", requireLauncherAdmin, asyncRoute(async (req, res) => {
  const result = await controlPlaneStore.deleteService(req.params.serviceId, req.nodedcSession.user);
  publishControlPlaneEvent("admin.service.deleted");
  res.json(result);
}));

app.post("/api/admin/access/grants", requireLauncherAdmin, asyncRoute(async (req, res) => {
  const result = await controlPlaneStore.upsertGrant(req.body, req.nodedcSession.user);
  const syncResult = await syncUsersToAuthentik(
    result.data,
    resolveGrantTargetUserIds(result.data, result.grant.targetType, result.grant.targetId),
    req.nodedcSession.user
  );
  publishControlPlaneEvent("admin.access.grant.updated", syncResult.userIds);
  res.json({ ...result, data: syncResult.data });
}));

app.post("/api/admin/access/exceptions", requireLauncherAdmin, asyncRoute(async (req, res) => {
  const result = await controlPlaneStore.upsertException(req.body, req.nodedcSession.user);
  const syncResult = await syncUsersToAuthentik(result.data, [result.exception.userId], req.nodedcSession.user);
  publishControlPlaneEvent("admin.access.exception.updated", syncResult.userIds);
  res.json({ ...result, data: syncResult.data });
}));

app.post("/api/admin/access/user-service", requireLauncherAdmin, asyncRoute(async (req, res) => {
  const result = await controlPlaneStore.setUserServiceAccess(req.body, req.nodedcSession.user);
  const syncResult = await syncUsersToAuthentik(result.data, [req.body?.userId], req.nodedcSession.user);
  publishControlPlaneEvent("admin.access.user-service.updated", syncResult.userIds);
  res.json({ ...result, data: syncResult.data });
}));

app.post("/api/admin/sync/:syncId/retry", requireLauncherAdmin, asyncRoute(async (req, res) => {
  const result = await controlPlaneStore.retrySync(req.params.syncId, req.nodedcSession.user);
  publishControlPlaneEvent("admin.sync.retry");
  res.json(result);
}));

app.get("/api/admin/sync/authentik/plan", requireLauncherAdmin, (_req, res) => {
  res.json(controlPlaneStore.buildAuthentikSyncPlan());
});

app.post("/api/storage/upload", asyncRoute(async (req, res) => {
  const result = await saveUploadedFile(req.body);
  res.json(result);
}));

app.post("/api/storage/data", requireLauncherAdmin, asyncRoute(async (req, res) => {
  await saveLauncherData(req.body);
  publishControlPlaneEvent("storage.data.updated");
  res.json({ ok: true, url: "/storage/launcher-data.json" });
}));

const vite = await createViteServer({
  root: projectRoot,
  appType: "spa",
  server: {
    middlewareMode: true,
    hmr: { server: httpServer },
  },
});

app.use(vite.middlewares);

app.use((error, _req, res, _next) => {
  vite.ssrFixStacktrace(error);
  const message = error instanceof Error ? error.message : "Unexpected server error";
  res.status(500).json({ error: message });
});

httpServer.listen(config.port, "0.0.0.0", () => {
  console.log(`NODE.DC launcher BFF listening on http://0.0.0.0:${config.port}`);
});

function readConfig() {
  const issuer = process.env.LAUNCHER_OIDC_ISSUER ?? "";
  const clientId = process.env.LAUNCHER_OIDC_CLIENT_ID ?? "";
  const clientSecret = process.env.LAUNCHER_OIDC_CLIENT_SECRET ?? "";
  const launcherDomain = process.env.LAUNCHER_DOMAIN ?? "localhost:5173";
  const appBaseUrl = process.env.LAUNCHER_BASE_URL ?? `http://${launcherDomain}`;

  return {
    port: Number(process.env.PORT ?? "5173"),
    issuer,
    clientId,
    clientSecret,
    redirectUri: process.env.LAUNCHER_OIDC_REDIRECT_URI ?? `${appBaseUrl}/auth/callback`,
    loggedOutRedirectUri: process.env.LAUNCHER_OIDC_LOGGED_OUT_REDIRECT_URI ?? `${appBaseUrl}/auth/logged-out`,
    appBaseUrl,
    scope: process.env.LAUNCHER_OIDC_SCOPE ?? "openid email profile groups offline_access",
    cookieDomain: process.env.LAUNCHER_COOKIE_DOMAIN || undefined,
    cookieSecure: process.env.COOKIE_SECURE === "true",
    oidcConfigured: Boolean(issuer && clientId && clientSecret),
    authentikBaseUrl:
      process.env.NODEDC_AUTHENTIK_BASE_URL ??
      process.env.AUTHENTIK_BASE_URL ??
      (process.env.AUTH_DOMAIN ? `http://${process.env.AUTH_DOMAIN}` : ""),
    authentikApiToken:
      process.env.NODEDC_AUTHENTIK_SERVICE_TOKEN ??
      process.env.AUTHENTIK_SERVICE_TOKEN ??
      process.env.AUTHENTIK_BOOTSTRAP_TOKEN ??
      "",
    internalAccessToken:
      process.env.NODEDC_INTERNAL_ACCESS_TOKEN ??
      process.env.NODEDC_PLATFORM_SERVICE_TOKEN ??
      process.env.PLANE_OIDC_CLIENT_SECRET ??
      "",
    taskLogoutUrl:
      process.env.TASK_LOGOUT_URL ??
      `${(process.env.TASK_BASE_URL ?? `http://${process.env.TASK_DOMAIN ?? "task.local.nodedc"}`).replace(/\/$/, "")}/logout`,
  };
}

function ensureOidcConfigured() {
  if (!config.oidcConfigured) {
    throw new Error("Launcher OIDC is not configured. Set LAUNCHER_OIDC_ISSUER, LAUNCHER_OIDC_CLIENT_ID and LAUNCHER_OIDC_CLIENT_SECRET.");
  }
}

async function getOidcDiscovery() {
  if (discoveryCache && discoveryCache.expiresAt > Date.now()) {
    return discoveryCache.discovery;
  }

  const discoveryUrl = new URL("./.well-known/openid-configuration", ensureTrailingSlash(config.issuer));
  const response = await fetch(discoveryUrl, { headers: { Accept: "application/json" } });

  if (!response.ok) {
    throw new Error(`Unable to load OIDC discovery from ${discoveryUrl}: HTTP ${response.status}`);
  }

  const discovery = await response.json();
  discoveryCache = { discovery, expiresAt: Date.now() + 5 * 60 * 1000 };
  return discovery;
}

async function exchangeCodeForTokens(discovery, code, codeVerifier) {
  const body = new URLSearchParams({
    grant_type: "authorization_code",
    code,
    redirect_uri: config.redirectUri,
    code_verifier: codeVerifier,
  });
  const response = await fetch(discovery.token_endpoint, {
    method: "POST",
    headers: {
      Authorization: `Basic ${Buffer.from(`${config.clientId}:${config.clientSecret}`).toString("base64")}`,
      "Content-Type": "application/x-www-form-urlencoded",
      Accept: "application/json",
    },
    body,
  });

  if (!response.ok) {
    const errorText = await response.text();
    throw new Error(`OIDC token exchange failed: HTTP ${response.status} ${errorText}`);
  }

  const tokenSet = await response.json();

  if (!tokenSet.id_token) {
    throw new Error("OIDC token response does not contain id_token");
  }

  return tokenSet;
}

async function verifyIdToken(discovery, idToken, nonce) {
  if (!jwksCache || jwksCache.uri !== discovery.jwks_uri) {
    jwksCache = {
      uri: discovery.jwks_uri,
      jwks: createRemoteJWKSet(new URL(discovery.jwks_uri)),
    };
  }

  const { payload } = await jwtVerify(idToken, jwksCache.jwks, {
    issuer: discovery.issuer ?? config.issuer,
    audience: config.clientId,
  });

  if (payload.nonce !== nonce) {
    throw new Error("OIDC nonce validation failed");
  }

  return payload;
}

function normalizeUser(claims) {
  const groups = normalizeGroups(claims.groups);
  const email = typeof claims.email === "string" ? claims.email : "";
  const avatarUrl = firstStringClaim(claims.picture, claims.avatar_url, claims.avatar);
  const name =
    typeof claims.name === "string" && claims.name
      ? claims.name
      : typeof claims.preferred_username === "string" && claims.preferred_username
        ? claims.preferred_username
        : email || String(claims.sub);

  return {
    sub: String(claims.sub),
    email,
    name,
    preferredUsername: typeof claims.preferred_username === "string" ? claims.preferred_username : null,
    avatarUrl,
    groups,
  };
}

function firstStringClaim(...values) {
  for (const value of values) {
    if (typeof value === "string" && value) return value;
  }

  return null;
}

function sanitizePassword(value) {
  return typeof value === "string" && value.length >= 8 ? value : null;
}

function sanitizeNewPassword(value) {
  if (typeof value !== "string" || value.length < 8) {
    throw new Error("Новый пароль должен быть не короче 8 символов");
  }

  return value;
}

function sanitizeSelfProfilePatch(payload) {
  return {
    name: payload?.name,
    email: payload?.email,
    phone: payload?.phone,
    position: payload?.position,
    avatarUrl: payload?.avatarUrl,
  };
}

function toProvisioningResponse(provisionedUser) {
  return {
    authentikUserId: provisionedUser.authentikUserId,
    email: provisionedUser.email,
    name: provisionedUser.name,
    groups: provisionedUser.groups,
    created: provisionedUser.created,
    temporaryPassword: provisionedUser.temporaryPassword,
  };
}

async function syncUsersToAuthentik(data, userIds, identity) {
  let latestData = data;
  const uniqueUserIds = [...new Set(userIds.filter((userId) => typeof userId === "string" && userId))];

  for (const userId of uniqueUserIds) {
    if (!latestData.users.some((user) => user.id === userId)) {
      continue;
    }

    const provisionedUser = await authentikSyncClient.provisionUser({ data: latestData, userId });
    const result = await controlPlaneStore.markUserAuthentikProvisioned(userId, provisionedUser, identity);
    latestData = result.data;
  }

  return { data: latestData, userIds: uniqueUserIds };
}

function resolveGrantTargetUserIds(data, targetType, targetId) {
  if (targetType === "user") {
    return [targetId];
  }

  if (targetType === "group") {
    return data.groups.find((group) => group.id === targetId)?.memberIds ?? [];
  }

  if (targetType === "client") {
    return data.memberships.filter((membership) => membership.clientId === targetId).map((membership) => membership.userId);
  }

  return [];
}

function publishControlPlaneEvent(action, affectedUserIds = []) {
  publishRuntimeEvent({
    type: "control-plane.updated",
    action,
    affectedUserIds: [...new Set(affectedUserIds.filter((userId) => typeof userId === "string" && userId))],
    emittedAt: new Date().toISOString(),
  });
}

function publishRuntimeEvent(payload) {
  const message = `event: nodedc-runtime\ndata: ${JSON.stringify(payload)}\n\n`;

  for (const client of runtimeEventClients) {
    try {
      client.res.write(message);
    } catch {
      runtimeEventClients.delete(client);
    }
  }
}

function normalizeGroups(groupsClaim) {
  if (Array.isArray(groupsClaim)) {
    return [...new Set(groupsClaim.filter((group) => typeof group === "string"))];
  }

  if (typeof groupsClaim === "string" && groupsClaim) {
    return [groupsClaim];
  }

  return [];
}

function getRuntimeSessionContext(session) {
  const fallback = {
    user: session.user,
    groups: session.user.groups,
  };

  try {
    const snapshot = controlPlaneStore.getSnapshot(session.user);

    if (snapshot.actor.source !== "launcher") {
      return fallback;
    }

    const user = snapshot.data.users.find((candidate) => candidate.id === snapshot.actor.id);

    if (!user) {
      return fallback;
    }

    const groups = resolveRequiredGroups(snapshot.data, user);

    return {
      groups,
      user: {
        ...session.user,
        email: user.email,
        name: user.name,
        avatarUrl: user.avatarUrl ?? session.user.avatarUrl,
        groups,
      },
    };
  } catch (error) {
    console.warn(error instanceof Error ? error.message : "Не удалось рассчитать runtime контекст Launcher");
    return fallback;
  }
}

function getAppsForSession(session) {
  return getAppsForUser(getRuntimeSessionContext(session).groups);
}

function getAppsForUser(userGroups) {
  const groupSet = new Set(userGroups);
  const catalog = getAppCatalog();

  return catalog.map((app) => {
    const matchedGroups = app.requiredGroups.filter((group) => groupSet.has(group));
    const isSuperAdmin = groupSet.has("nodedc:superadmin");
    const isPublic = app.requiredGroups.length === 0;
    const hasAccess = isSuperAdmin || isPublic || matchedGroups.length > 0;

    return {
      ...app,
      matchedGroups: isSuperAdmin ? ["nodedc:superadmin", ...matchedGroups] : matchedGroups,
      hasAccess,
      accessReason: hasAccess ? "Доступ подтверждён" : "Нет доступа",
    };
  });
}

function getAppCatalog() {
  const launcherData = readLauncherData();
  const services = Array.isArray(launcherData?.services) ? launcherData.services : [];
  const serviceCatalog = services.map((service) => {
    const specialGroups = specialRequiredGroups(service.slug);
    const requiredGroups = specialGroups.length
      ? specialGroups
      : service.authentikGroupName
        ? [service.authentikGroupName]
        : [];

    return {
      id: service.id,
      slug: service.slug,
      title: service.title,
      description: service.description,
      url: getServiceUrl(service),
      openUrl: getServiceUrl(service),
      status: service.status ?? "disabled",
      provider: "authentik",
      requiredGroups,
      media: {
        icon: service.iconUrl ?? null,
        coverImage: service.coverImageUrl ?? null,
        accentColor: service.accentColor ?? null,
      },
    };
  });

  return [
    {
      id: "launcher",
      slug: "launcher",
      title: "NODE.DC Launcher",
      description: "Единая точка входа в приложения NODE.DC.",
      url: config.appBaseUrl,
      openUrl: config.appBaseUrl,
      status: "active",
      provider: "authentik",
      requiredGroups: ["nodedc:launcher:admin", "nodedc:launcher:user"],
    },
    ...serviceCatalog.filter((service) => service.slug !== "launcher"),
  ];
}

function specialRequiredGroups(slug) {
  if (slug === "launcher") return ["nodedc:launcher:admin", "nodedc:launcher:user"];
  if (slug === "task-manager") return ["nodedc:taskmanager:admin", "nodedc:taskmanager:user"];
  return [];
}

function getServiceUrl(service) {
  if (service.slug === "task-manager") {
    const taskBaseUrl = process.env.TASK_BASE_URL ?? `http://${process.env.TASK_DOMAIN ?? "task.local.nodedc"}`;
    return `${taskBaseUrl.replace(/\/$/, "")}/auth/oidc/login/`;
  }

  return service.launchUrl || service.url || "#";
}

function getFrontchannelLogoutUrls() {
  const urls = [config.taskLogoutUrl];
  const launcherData = readLauncherData();
  const services = Array.isArray(launcherData?.services) ? launcherData.services : [];

  for (const service of services) {
    if (typeof service.logoutUrl === "string" && service.logoutUrl.trim()) {
      urls.push(service.logoutUrl.trim());
    }
  }

  return [...new Set(urls.map(normalizeLogoutUrl).filter(Boolean))];
}

function normalizeLogoutUrl(value) {
  try {
    const url = new URL(value);
    if (url.protocol !== "http:" && url.protocol !== "https:") return null;
    return url.toString();
  } catch {
    return null;
  }
}

function renderGlobalLogoutPage(frontchannelLogoutUrls, finalRedirectUrl) {
  const logoutUrlsJson = JSON.stringify(frontchannelLogoutUrls);
  const redirectUrlJson = JSON.stringify(finalRedirectUrl);

  return `<!doctype html>
<html lang="ru">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Выход из NODE.DC</title>
    <style>
      html,body{height:100%;margin:0;background:#050606;color:#f4f4f4;font-family:Inter,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}
      body{display:grid;place-items:center}
      main{max-width:28rem;padding:2rem;text-align:center}
      h1{margin:0 0 .75rem;font-size:1.5rem}
      p{margin:0;color:#a6a6a6;line-height:1.5}
    </style>
  </head>
  <body>
    <main>
      <h1>Выходим из NODE.DC</h1>
      <p>Закрываем сессии подключённых приложений и платформенный вход.</p>
    </main>
    <script>
      const eventPayload = {
        type: "nodedc:session:logout",
        id: globalThis.crypto?.randomUUID ? globalThis.crypto.randomUUID() : String(Date.now()) + "-" + Math.random().toString(36).slice(2),
        source: "launcher-global-logout",
        createdAt: Date.now()
      };
      try {
        const channel = new BroadcastChannel("nodedc-platform-session");
        channel.postMessage(eventPayload);
        channel.close();
      } catch {}
      try {
        localStorage.setItem("nodedc:platform-session-event", JSON.stringify(eventPayload));
      } catch {}
      const logoutUrls = ${logoutUrlsJson};
      const finalRedirectUrl = ${redirectUrlJson};
      for (const logoutUrl of logoutUrls) {
        fetch(logoutUrl, { mode: "no-cors", credentials: "include", keepalive: true }).catch(() => undefined);
        const image = new Image();
        image.referrerPolicy = "no-referrer";
        image.src = logoutUrl;
      }
      window.setTimeout(() => window.location.replace(finalRedirectUrl), 900);
    </script>
  </body>
</html>`;
}

function renderSessionSyncBridgePage(allowedOrigins) {
  const allowedOriginsJson = JSON.stringify(allowedOrigins);

  return `<!doctype html>
<html lang="ru">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>NODE.DC session sync</title>
  </head>
  <body>
    <script>
      const allowedOrigins = new Set(${allowedOriginsJson});
      const parentOrigin = (() => {
        try {
          return new URL(document.referrer).origin;
        } catch {
          return "";
        }
      })();
      const channelName = "nodedc-platform-session";
      const storageKey = "nodedc:platform-session-event";
      let lastEventId = null;

      function isAllowedOrigin(origin) {
        return allowedOrigins.has(origin);
      }

      function isLogoutEvent(payload) {
        return payload && payload.type === "nodedc:session:logout" && typeof payload.id === "string";
      }

      function forwardToParent(payload) {
        if (!isLogoutEvent(payload) || payload.id === lastEventId || !isAllowedOrigin(parentOrigin)) return;
        lastEventId = payload.id;
        window.parent.postMessage(payload, parentOrigin);
      }

      function publish(payload) {
        if (!isLogoutEvent(payload)) return;
        try {
          const channel = new BroadcastChannel(channelName);
          channel.postMessage(payload);
          channel.close();
        } catch {}
        try {
          localStorage.setItem(storageKey, JSON.stringify(payload));
        } catch {}
      }

      window.addEventListener("message", (event) => {
        if (!isAllowedOrigin(event.origin) || !isLogoutEvent(event.data)) return;
        publish(event.data);
      });

      try {
        const channel = new BroadcastChannel(channelName);
        channel.addEventListener("message", (event) => forwardToParent(event.data));
      } catch {}

      window.addEventListener("storage", (event) => {
        if (event.key !== storageKey || !event.newValue) return;
        try {
          forwardToParent(JSON.parse(event.newValue));
        } catch {}
      });
    </script>
  </body>
</html>`;
}

function getSessionSyncAllowedOrigins() {
  const origins = new Set([new URL(config.appBaseUrl).origin]);

  for (const logoutUrl of getFrontchannelLogoutUrls()) {
    try {
      origins.add(new URL(logoutUrl).origin);
    } catch {
      void 0;
    }
  }

  return [...origins];
}

function readLauncherData() {
  const dataPath = join(projectRoot, "public", "storage", "launcher-data.json");

  try {
    if (!existsSync(dataPath)) return null;
    return JSON.parse(readFileSync(dataPath, "utf8"));
  } catch {
    return null;
  }
}

async function saveUploadedFile(payload) {
  if (!isUploadPayload(payload)) {
    throw new Error("Некорректный payload загрузки");
  }

  const match = /^data:([^;,]+)?(?:;[^,]*)?;base64,(.*)$/s.exec(payload.dataUrl);

  if (!match) {
    throw new Error("Файл должен прийти data-url с base64");
  }

  const mimeType = payload.mimeType || match[1] || "application/octet-stream";
  const storedName = buildStoredFileName(payload.fileName, mimeType);
  const fileBuffer = Buffer.from(match[2], "base64");

  await Promise.all(
    getWritableStorageRoots().map(async (storageRoot) => {
      const uploadDir = join(storageRoot, "uploads");
      await mkdir(uploadDir, { recursive: true });
      await writeFile(join(uploadDir, storedName), fileBuffer);
    })
  );

  return {
    ok: true,
    url: `/storage/uploads/${storedName}`,
    fileName: storedName,
    originalFileName: payload.fileName,
    mimeType,
  };
}

async function saveLauncherData(payload) {
  await controlPlaneStore.writeData(payload);
}

function getWritableStorageRoots() {
  const roots = [join(projectRoot, "public", "storage")];
  const distRoot = join(projectRoot, "dist");

  if (existsSync(distRoot)) {
    roots.push(join(distRoot, "storage"));
  }

  return roots;
}

function buildStoredFileName(fileName, mimeType) {
  const extension = extname(fileName) || extensionFromMimeType(mimeType);
  const rawBase = fileName.slice(0, extension ? -extension.length : undefined);
  const safeBase =
    rawBase
      .normalize("NFKD")
      .replace(/[^\w.-]+/g, "-")
      .replace(/^-+|-+$/g, "")
      .slice(0, 80) || "upload";

  return `${Date.now()}-${randomUUID().slice(0, 8)}-${safeBase}${extension.toLowerCase()}`;
}

function extensionFromMimeType(mimeType) {
  if (mimeType === "image/jpeg") return ".jpg";
  if (mimeType === "image/png") return ".png";
  if (mimeType === "image/gif") return ".gif";
  if (mimeType === "image/webp") return ".webp";
  if (mimeType === "video/mp4") return ".mp4";
  if (mimeType === "video/webm") return ".webm";
  if (mimeType === "video/quicktime") return ".mov";
  return "";
}

function isUploadPayload(payload) {
  return Boolean(
    payload &&
      typeof payload === "object" &&
      typeof payload.fileName === "string" &&
      typeof payload.mimeType === "string" &&
      typeof payload.dataUrl === "string"
  );
}

function getCurrentSession(req) {
  const sessionId = parseCookies(req.headers.cookie)[sessionCookieName];

  if (!sessionId) return null;

  const session = sessions.get(sessionId);

  if (!session || session.expiresAt < Date.now()) {
    sessions.delete(sessionId);
    return null;
  }

  return session;
}

function pruneExpiredSessions() {
  for (const [sessionId, session] of sessions) {
    if (session.expiresAt < Date.now()) {
      sessions.delete(sessionId);
    }
  }
}

function pruneExpiredState() {
  for (const [state, pendingLogin] of pendingLogins) {
    if (pendingLogin.expiresAt < Date.now()) {
      pendingLogins.delete(state);
    }
  }
}

function getValidOidcCookieStates(req) {
  const rawValue = parseCookies(req.headers.cookie)[oidcStateCookieName];

  if (!rawValue) return [];

  const seen = new Set();

  return rawValue
    .split(".")
    .filter((state) => /^[A-Za-z0-9_-]{32,256}$/.test(state))
    .filter((state) => {
      if (seen.has(state)) return false;
      seen.add(state);
      return true;
    })
    .filter((state) => {
      const pendingLogin = pendingLogins.get(state);
      return Boolean(pendingLogin && pendingLogin.expiresAt >= Date.now());
    });
}

function setOidcStateCookie(res, states) {
  if (!states.length) {
    res.clearCookie(oidcStateCookieName, clearCookieOptions());
    return;
  }

  res.cookie(oidcStateCookieName, states.join("."), cookieOptions(pendingLoginTtlMs));
}

function parseCookies(cookieHeader) {
  if (!cookieHeader) return {};

  return Object.fromEntries(
    cookieHeader.split(";").flatMap((part) => {
      const separatorIndex = part.indexOf("=");
      if (separatorIndex === -1) return [];
      const key = part.slice(0, separatorIndex).trim();
      const value = part.slice(separatorIndex + 1).trim();
      return [[key, decodeURIComponent(value)]];
    })
  );
}

function requireLauncherAdmin(req, res, next) {
  const session = getCurrentSession(req);

  if (!session) {
    res.status(401).json({ authenticated: false, loginUrl: "/auth/login" });
    return;
  }

  const runtimeContext = getRuntimeSessionContext(session);

  if (!isLauncherAdmin(runtimeContext.groups)) {
    res.status(403).json({ error: "Недостаточно прав Launcher admin" });
    return;
  }

  req.nodedcSession = { ...session, user: runtimeContext.user };
  next();
}

function requireSession(req, res, next) {
  const session = getCurrentSession(req);

  if (!session) {
    res.status(401).json({ authenticated: false, loginUrl: "/auth/login" });
    return;
  }

  const runtimeContext = getRuntimeSessionContext(session);
  req.nodedcSession = { ...session, user: runtimeContext.user };
  next();
}

function isInternalRequestAuthorized(req) {
  if (!config.internalAccessToken) {
    return false;
  }

  const authorization = typeof req.headers.authorization === "string" ? req.headers.authorization : "";
  const bearerToken = authorization.match(/^Bearer\s+(.+)$/i)?.[1] ?? "";
  const headerToken = typeof req.headers["x-nodedc-internal-token"] === "string" ? req.headers["x-nodedc-internal-token"] : "";
  const requestToken = bearerToken || headerToken;

  return safeTokenEquals(requestToken, config.internalAccessToken);
}

function safeTokenEquals(actual, expected) {
  if (!actual || !expected) {
    return false;
  }

  const actualBuffer = Buffer.from(String(actual));
  const expectedBuffer = Buffer.from(String(expected));

  return actualBuffer.length === expectedBuffer.length && timingSafeEqual(actualBuffer, expectedBuffer);
}

function findInternalAccessUser(data, payload) {
  const subject = typeof payload?.subject === "string" ? payload.subject : "";
  const email = typeof payload?.email === "string" ? payload.email.toLowerCase() : "";
  const userId = typeof payload?.userId === "string" ? payload.userId : "";

  return (
    data.users.find((user) => userId && user.id === userId) ??
    data.users.find((user) => subject && user.authentikUserId === subject) ??
    data.users.find((user) => email && user.email.toLowerCase() === email) ??
    null
  );
}

function sanitizeServiceSlug(value) {
  return typeof value === "string" && value ? value : "task-manager";
}

function getLauncherProfileContext(session) {
  const snapshot = controlPlaneStore.getSnapshot(session.user);

  if (snapshot.actor.source !== "launcher") {
    throw new Error("Профиль пользователя не найден в Launcher control-plane");
  }

  return {
    actor: snapshot.actor,
    data: snapshot.data,
  };
}

function findLauncherUser(data, userId) {
  const user = data.users.find((candidate) => candidate.id === userId);

  if (!user) {
    throw new Error(`Unknown Launcher user: ${userId}`);
  }

  return user;
}

function isLauncherAdmin(groups) {
  return groups.includes("nodedc:superadmin") || groups.includes("nodedc:launcher:admin");
}

function cookieOptions(maxAgeMs) {
  const options = {
    httpOnly: true,
    sameSite: "lax",
    secure: config.cookieSecure,
    path: "/",
    maxAge: maxAgeMs,
  };

  if (config.cookieDomain) {
    options.domain = config.cookieDomain;
  }

  return options;
}

function clearCookieOptions() {
  const options = {
    httpOnly: true,
    sameSite: "lax",
    secure: config.cookieSecure,
    path: "/",
  };

  if (config.cookieDomain) {
    options.domain = config.cookieDomain;
  }

  return options;
}

function setNoStore(res) {
  res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0");
  res.setHeader("Pragma", "no-cache");
  res.setHeader("Expires", "0");
}

function buildLoginRedirectUrl(returnTo, { forceLogin = false } = {}) {
  const loginUrl = new URL("/auth/login", config.appBaseUrl);
  const cleanReturnTo = sanitizeReturnTo(returnTo);

  if (forceLogin) {
    loginUrl.searchParams.set("prompt", "login");
  }

  if (cleanReturnTo !== "/") {
    loginUrl.searchParams.set("returnTo", cleanReturnTo);
  }

  return loginUrl.toString();
}

function buildLoggedOutRedirectUrl() {
  const loggedOutUrl = new URL(config.loggedOutRedirectUri);
  return loggedOutUrl.toString();
}

function randomBase64Url(size) {
  return randomBytes(size).toString("base64url");
}

function sanitizeReturnTo(returnTo) {
  if (typeof returnTo !== "string" || !returnTo.startsWith("/") || returnTo.startsWith("//")) {
    return "/";
  }

  return returnTo;
}

function sanitizePrompt(prompt) {
  if (prompt === "login" || prompt === "none" || prompt === "consent" || prompt === "select_account") {
    return prompt;
  }

  return null;
}

function ensureTrailingSlash(value) {
  return value.endsWith("/") ? value : `${value}/`;
}

function asyncRoute(handler) {
  return (req, res, next) => {
    Promise.resolve(handler(req, res, next)).catch(next);
  };
}

function loadEnvFiles(candidates) {
  for (const candidate of candidates) {
    if (!candidate) continue;

    const envPath = resolve(projectRoot, candidate);

    if (!existsSync(envPath)) continue;

    const lines = readFileSync(envPath, "utf8").split(/\r?\n/);

    for (const line of lines) {
      const trimmed = line.trim();

      if (!trimmed || trimmed.startsWith("#") || !trimmed.includes("=")) continue;

      const separatorIndex = trimmed.indexOf("=");
      const key = trimmed.slice(0, separatorIndex).trim();
      const value = stripEnvQuotes(trimmed.slice(separatorIndex + 1).trim());

      if (!process.env[key]) {
        process.env[key] = value;
      }
    }
  }
}

function stripEnvQuotes(value) {
  if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
    return value.slice(1, -1);
  }

  return value;
}
