import { createHybridAuthStorage as b, AuthSDK as I, NextJSCookieContext as R } from "@frank-auth/sdk";
import { NextResponse as m, NextRequest as S } from "next/server";
const w = {
  apiUrl: "http://localhost:8990",
  sessionCookieName: "frank_sid",
  storageKeyPrefix: "frank_auth_",
  publicPaths: [],
  privatePaths: [],
  authPaths: [
    "/sign-in",
    "/sign-up",
    "/forgot-password",
    "/verify-email",
    "/reset-password"
  ],
  skipApiCallOnNetworkError: !1,
  maxRetries: 2,
  apiTimeout: 5e3,
  fallbackToLocalTokens: !0,
  offlineMode: !1,
  allPathsPrivate: !0,
  signInPath: "/sign-in",
  signUpPath: "/sign-up",
  afterSignInPath: "/dashboard",
  afterSignUpPath: "/dashboard",
  afterSignOutPath: "/",
  orgSelectionPath: "/select-organization",
  debug: !1,
  enableOrgRouting: !1,
  ignorePaths: [
    "/api",
    "/_next",
    "/favicon.ico",
    "/images",
    "/static",
    "/_vercel"
  ],
  cookieOptions: {
    secure: process.env.NODE_ENV === "production",
    httpOnly: !0,
    sameSite: "lax",
    maxAge: 60 * 60 * 24 * 7
    // 7 days
  }
};
function A(s, t) {
  return t.some((e) => {
    if (e === s) return !0;
    if (e.endsWith("*")) {
      const r = e.slice(0, -1);
      return s.startsWith(r);
    }
    return e.startsWith("/") && e.endsWith("/") ? new RegExp(e.slice(1, -1)).test(s) : !1;
  });
}
function O(s, t, e) {
  const r = s.cookies.getAll(), k = {
    cookies: {
      ...Object.fromEntries(
        r.map((a) => [a.name, a.value])
      ),
      // Also provide a get method for Next.js cookie compatibility
      get: (a) => s.cookies.get(a),
      getAll: () => s.cookies.getAll()
    }
  }, p = {
    setHeader: (a, o) => {
      if (a === "Set-Cookie") {
        const u = Array.isArray(o) ? o : [o];
        for (const n of u) {
          const [l, ...h] = n.split(";"), [d, i] = l.split("=");
          if (d && i) {
            const f = {
              httpOnly: e.cookieOptions.httpOnly,
              secure: e.cookieOptions.secure,
              sameSite: e.cookieOptions.sameSite,
              maxAge: e.cookieOptions.maxAge,
              path: "/"
              // Ensure path is set
            };
            for (const x of h) {
              const [g, P] = x.trim().split("=");
              switch (g.toLowerCase()) {
                case "max-age":
                  f.maxAge = Number.parseInt(P, 10);
                  break;
                case "expires":
                  f.expires = new Date(P);
                  break;
                case "path":
                  f.path = P;
                  break;
                case "domain":
                  f.domain = P;
                  break;
                case "secure":
                  f.secure = !0;
                  break;
                case "httponly":
                  f.httpOnly = !0;
                  break;
                case "samesite":
                  f.sameSite = P;
                  break;
              }
            }
            t.cookies.set(
              d.trim(),
              i.trim(),
              f
            );
          }
        }
      } else
        t.headers.set(
          a,
          Array.isArray(o) ? o.join(", ") : o
        );
    },
    getHeader: (a) => t.headers.get(a)
  };
  return new R(k, p);
}
function T(s, t, e) {
  O(t, e, s);
  const r = b(s.storageKeyPrefix, {
    req: t,
    res: {
      setHeader: (p, a) => {
        if (p === "Set-Cookie") {
          const o = Array.isArray(a) ? a : [a];
          for (const u of o) {
            const [n, ...l] = u.split(";"), [h, d] = n.split("=");
            if (h && d) {
              const i = {
                httpOnly: s.cookieOptions.httpOnly,
                secure: s.cookieOptions.secure,
                sameSite: s.cookieOptions.sameSite,
                maxAge: s.cookieOptions.maxAge
              };
              for (const f of l) {
                const [x, g] = f.trim().split("=");
                switch (x.toLowerCase()) {
                  case "max-age":
                    i.maxAge = Number.parseInt(g, 10);
                    break;
                  case "expires":
                    i.expires = new Date(g);
                    break;
                  case "path":
                    i.path = g;
                    break;
                  case "domain":
                    i.domain = g;
                    break;
                  case "secure":
                    i.secure = !0;
                    break;
                  case "httponly":
                    i.httpOnly = !0;
                    break;
                  case "samesite":
                    i.sameSite = g;
                    break;
                }
              }
              e.cookies.set(
                h.trim(),
                d.trim(),
                i
              );
            }
          }
        } else
          e.headers.set(
            p,
            Array.isArray(a) ? a.join(", ") : a
          );
      },
      getHeader: (p) => e.headers.get(p)
    }
  });
  return c(s, "Storage tokens:", {
    accessToken: r.getAccessToken() ? "[PRESENT]" : "[MISSING]",
    refreshToken: r.getRefreshToken() ? "[PRESENT]" : "[MISSING]",
    sessionId: r.getSessionId() ? "[PRESENT]" : "[MISSING]",
    remoteSessionCookie: t.cookies.get("frank_sid")?.value,
    storageKeyPrefix: s.storageKeyPrefix,
    userType: s.userType,
    projectId: s.projectId,
    secretKey: s.secretKey,
    apiUrl: s.apiUrl
  }), new I({
    apiUrl: s.apiUrl,
    publishableKey: s.publishableKey,
    sessionCookieName: s.sessionCookieName,
    storageKeyPrefix: s.storageKeyPrefix,
    userType: s.userType,
    projectId: s.projectId,
    secretKey: s.secretKey,
    storage: r,
    debug: s.debug,
    debugConfig: {
      logLevel: "debug"
    }
  });
}
async function y(s, t, e) {
  try {
    c(e, "Validating authentication using AuthSDK");
    const r = !!t.authStorage.getAccessToken(), k = !!t.authStorage.getRefreshToken(), a = !!s.cookies.get(e.sessionCookieName)?.value;
    if (c(e, "Authentication state:", {
      hasAccessToken: r,
      hasRefreshToken: k,
      hasSessionCookie: a,
      sessionCookieName: e.sessionCookieName
    }), !r && !k && !a)
      return c(e, "No tokens or session cookies found, skipping API call"), {
        isAuthenticated: !1,
        user: null,
        session: null,
        organizationId: null,
        error: null,
        tokenInfo: {
          accessTokenExpired: !0,
          refreshTokenExpired: !0,
          canRefresh: !1
        }
      };
    const o = t.getTokenExpirationInfo();
    if (c(e, "Token expiration info:", {
      accessToken: {
        isExpired: o.accessToken.isExpired,
        expiresIn: o.accessToken.expiresIn
      },
      refreshToken: {
        isExpired: o.refreshToken.isExpired,
        expiresIn: o.refreshToken.expiresIn
      }
    }), e.skipApiCallOnNetworkError && process.env.NODE_ENV === "development" && (c(
      e,
      "Skipping API call due to development mode network configuration"
    ), r && !o.accessToken.isExpired || a))
      return {
        isAuthenticated: !0,
        user: null,
        // We don't have user data without API call
        session: null,
        organizationId: e.projectId || null,
        error: null,
        tokenInfo: {
          accessTokenExpired: o.accessToken.isExpired,
          refreshTokenExpired: o.refreshToken.isExpired,
          canRefresh: !o.refreshToken.isExpired
        }
      };
    const u = async () => {
      const d = new AbortController(), i = setTimeout(() => d.abort(), 5e3), f = s.cookies.getAll().map((x) => `${x.name}=${x.value}`).join("; ");
      try {
        const x = await t.getAuthStatus({
          credentials: "include",
          signal: d.signal,
          headers: {
            "User-Agent": "FrankAuth-Middleware/1.0",
            Accept: "application/json",
            "Content-Type": "application/json",
            // Copy essential headers only
            "X-Forwarded-For": s.headers.get("x-forwarded-for") || "",
            "X-Real-IP": s.headers.get("x-real-ip") || "",
            Cookie: f
          },
          // Add retry configuration
          cache: "no-cache",
          keepalive: !1
        });
        return clearTimeout(i), x;
      } catch (x) {
        throw clearTimeout(i), x;
      }
    };
    let n, l = null;
    const h = e.maxRetries || 2;
    for (let d = 1; d <= h; d++)
      try {
        c(e, `Auth status attempt ${d}/${h}`), n = await u();
        break;
      } catch (i) {
        if (l = i, c(e, `Auth status attempt ${d} failed:`, {
          name: i.name,
          message: i.message,
          code: i.code
        }), d === h) {
          if (e.fallbackToLocalTokens && (r && !o.accessToken.isExpired || a))
            return c(
              e,
              "API failed but local token/session is valid, trusting local state"
            ), {
              isAuthenticated: !0,
              user: null,
              session: null,
              organizationId: e.projectId || null,
              error: i,
              tokenInfo: {
                accessTokenExpired: o.accessToken.isExpired,
                refreshTokenExpired: o.refreshToken.isExpired,
                canRefresh: !o.refreshToken.isExpired
              }
            };
          throw i;
        }
        if (!N(i))
          throw i;
        const f = Math.min(1e3 * Math.pow(2, d - 1), 5e3), x = Math.random() * 0.1 * f;
        await new Promise((g) => setTimeout(g, f + x));
      }
    return c(e, "Auth status received:", {
      isAuthenticated: n.isAuthenticated,
      hasUser: !!n.user,
      organizationId: n.organizationId
    }), {
      isAuthenticated: n.isAuthenticated,
      user: n.user || null,
      session: n.session || null,
      organizationId: n.organizationId || null,
      error: null,
      tokenInfo: {
        accessTokenExpired: o.accessToken.isExpired,
        refreshTokenExpired: o.refreshToken.isExpired,
        canRefresh: !o.refreshToken.isExpired
      }
    };
  } catch (r) {
    c(e, "Authentication validation error:", {
      name: r.name,
      message: r.message,
      code: r.code
    });
    const k = t.getTokenExpirationInfo();
    return {
      isAuthenticated: !1,
      user: null,
      session: null,
      organizationId: null,
      error: r,
      tokenInfo: {
        // Don't mark tokens as expired just because of network errors
        accessTokenExpired: k.accessToken.isExpired,
        refreshTokenExpired: k.refreshToken.isExpired,
        canRefresh: !k.refreshToken.isExpired && k.refreshToken.isValid
      }
    };
  }
}
function N(s) {
  const t = [
    "NETWORK_ERROR",
    "ECONNREFUSED",
    "ENOTFOUND",
    "ECONNRESET",
    "ETIMEDOUT",
    "AbortError"
  ];
  return s?.code && t.includes(s.code) || s?.name === "FrankAuthNetworkError" || s?.message?.includes("fetch failed") || s?.message?.includes("network") || s?.name === "AbortError";
}
function C(s, t) {
  if (!t.enableOrgRouting) return null;
  const e = s.nextUrl.hostname;
  if (t.customDomain && e === t.customDomain)
    return s.nextUrl.searchParams.get("org") || null;
  const r = e.split(".");
  return r.length > 2 ? r[0] : null;
}
function c(s, t, e) {
  s.debug && console.log(`[FrankAuth Middleware] ${t}`, e || "");
}
async function U(s, t) {
  const e = s.nextUrl.pathname;
  if (c(t, `Processing request: ${e}`), A(e, t.ignorePaths))
    return c(t, `Ignoring path: ${e}`), m.next();
  const r = m.next();
  if (t.hooks?.beforeAuth) {
    const h = await t.hooks.beforeAuth(s);
    if (h instanceof m) return h;
    h instanceof S && (s = h);
  }
  const k = T(t, s, r), p = A(e, t.publicPaths), a = A(e, t.authPaths);
  let o;
  t.allPathsPrivate ? o = !p && !a : o = A(e, t.privatePaths), c(t, "Path analysis:", {
    isPublicPath: p,
    isPrivatePath: o,
    isAuthPath: a,
    allPathsPrivate: t.allPathsPrivate
  });
  const u = await y(s, k, t);
  c(t, "Authentication result:", {
    isAuthenticated: u.isAuthenticated,
    hasUser: !!u.user,
    organizationId: u.organizationId,
    tokenInfo: u.tokenInfo
  });
  const l = await v({
    req: s,
    config: t,
    auth: u,
    path: e,
    isPublicPath: p,
    isPrivatePath: o,
    isAuthPath: a,
    response: r
  });
  if (t.hooks?.afterAuth) {
    const h = await t.hooks.afterAuth(s, l, u);
    if (h instanceof m) return h;
  }
  return l;
}
async function v(s) {
  const {
    req: t,
    config: e,
    auth: r,
    path: k,
    isPublicPath: p,
    isPrivatePath: a,
    isAuthPath: o,
    response: u
  } = s;
  try {
    if (r.isAuthenticated && r.user) {
      if (c(e, "User is authenticated"), e.hooks?.onAuthenticated && r.session) {
        const n = await e.hooks.onAuthenticated(
          t,
          r.user,
          r.session
        );
        if (n instanceof m) return n;
      }
      if (o) {
        const n = t.nextUrl.searchParams.get("redirect_url") || e.afterSignInPath;
        c(
          e,
          `Redirecting authenticated user from auth path to: ${n}`
        );
        const l = m.redirect(
          new URL(n, t.url)
        );
        return E(u, l), l;
      }
      if (e.enableOrgRouting && !r.organizationId && k !== e.orgSelectionPath) {
        if (c(e, "Organization required but not selected"), e.hooks?.onOrganizationRequired) {
          const l = await e.hooks.onOrganizationRequired(
            t,
            r.user
          );
          if (l instanceof m) return l;
        }
        const n = m.redirect(
          new URL(e.orgSelectionPath, t.url)
        );
        return E(u, n), n;
      }
      return u;
    }
    if (c(e, "User is not authenticated"), e.hooks?.onUnauthenticated) {
      const n = await e.hooks.onUnauthenticated(t);
      if (n instanceof m) return n;
    }
    if (p || o)
      return c(e, "Allowing access to public/auth path"), u;
    if (a) {
      const n = new URL(e.signInPath, t.url);
      n.searchParams.set(
        "redirect_url",
        t.nextUrl.pathname + t.nextUrl.search
      ), c(e, `Redirecting to sign in: ${n.toString()}`);
      const l = m.redirect(n);
      return E(u, l), l;
    }
    return u;
  } catch (n) {
    if (c(e, "Error in authentication handling:", n), e.hooks?.onError) {
      const d = await e.hooks.onError(t, n);
      if (d instanceof m) return d;
    }
    const l = new URL(e.signInPath, t.url);
    l.searchParams.set("error", "auth_error");
    const h = m.redirect(l);
    return E(u, h), h;
  }
}
function E(s, t) {
  try {
    const e = s.headers.getSetCookie();
    if (e.length > 0)
      for (const r of e)
        t.headers.append("Set-Cookie", r);
    for (const r of s.cookies.getAll())
      r.name && r.value && t.cookies.set(r.name, r.value, {
        domain: r.domain,
        expires: r.expires,
        httpOnly: r.httpOnly,
        maxAge: r.maxAge,
        path: r.path || "/",
        // Ensure path is always set
        secure: r.secure,
        sameSite: r.sameSite
      });
  } catch (e) {
    console.error("Error copying response cookies:", e);
  }
}
function D(s) {
  const t = {
    ...w,
    ...s
  };
  if (!t.publishableKey)
    throw new Error("publishableKey is required for Frank Auth middleware");
  return t.storageKeyPrefix || (t.storageKeyPrefix = "frank_auth"), c(t, "Frank Auth middleware initialized with config:", {
    publicPaths: t.publicPaths,
    privatePaths: t.privatePaths,
    authPaths: t.authPaths,
    allPathsPrivate: t.allPathsPrivate,
    signInPath: t.signInPath,
    enableOrgRouting: t.enableOrgRouting,
    storageKeyPrefix: t.storageKeyPrefix
  }), async function(r) {
    return U(r, t);
  };
}
function _(s) {
  return function(e) {
    return s.custom ? s.custom(e) : s.exclude && A(e, s.exclude) ? !1 : !!(s.include && A(e, s.include));
  };
}
async function j(s, t, e) {
  try {
    const r = m.next(), k = T(
      e,
      s,
      r
    ), p = s.cookies.getAll().map((o) => `${o.name}=${o.value}`).join("; ");
    return (await k.getAuthStatus({
      headers: {
        Cookie: p
      }
    })).isAuthenticated;
  } catch {
    return !1;
  }
}
function F(s, t) {
  return C(s, t);
}
function M(s, t) {
  const e = m.next();
  return T(t, s, e);
}
async function L(s, t) {
  const e = m.next(), r = T(
    t,
    s,
    e
  );
  return y(
    s,
    r,
    t
  );
}
export {
  L as checkAuthStatus,
  j as checkPermission,
  D as createFrankAuthMiddleware,
  _ as createMatcher,
  M as getAuthSDKFromRequest,
  F as getOrganizationFromRequest
};
//# sourceMappingURL=index.js.map
