import { PUBLIC_API_URL } from "$env/static/public";
import { redirect, type RequestEvent } from "@sveltejs/kit";
import axios from "axios";
import api from "../axios";
import { io } from "socket.io-client";
import { browser } from "$app/environment";

export const user = {
  isLoggedIn(): boolean {
    // This function can't reliably check authentication status in client-side code
    // since it doesn't have access to httpOnly cookies.
    // It should be used only in server-side contexts like load functions.
    if (browser) {
      console.warn(
        "user.isLoggedIn() called in browser context - this may not work as expected"
      );
      return false;
    }

    // In server context, this would need the request event to be passed in
    // Rather than using getRequestEvent() which makes this module server-only
    throw new Error(
      "user.isLoggedIn() should not be called without a request event. Use isLoggedInServer() instead."
    );
  },

  // Server-side version that takes a request event parameter
  isLoggedInServer(event: RequestEvent): boolean {
    const authToken = event.cookies.get("accessToken");
    return !!authToken;
  },

  async refreshSession(refreshToken: string) {
    const refreshedTokens = (
      await axios.post(
        `${PUBLIC_API_URL}/v1/auth/refresh`,
        {},
        {
          headers: {
            "x-refresh-token": refreshToken,
          },
        }
      )
    ).data.data;

    return refreshedTokens;
  },

  async fetchInfo(accessToken: string) {
    const response = await api.get("/v1/user/@me", {
      headers: {
        Authorization: `Bearer ${accessToken}`,
      },
    });
    return response.data;
  },

  logout(event: RequestEvent) {
    if (!event) return;

    // Clear authentication cookies
    event.cookies.delete("accessToken", { path: "/" });
    event.cookies.delete("refreshToken", { path: "/" });

    return redirect(303, "/login");
  },

  async checkPermissions(accessToken: string, permissions: string[]) {
    const response = await api.post(
      "/v1/user/@me/check-permission",
      { permissions },
      {
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      }
    );

    return response.data;
  },

  /**
   * Polls the API until the Microsoft OAuth callback has been processed and
   * tokens are available, then returns them.  Uses a simple HTTP long-poll
   * pattern instead of socket.io — much more reliable through reverse proxies.
   */
  async awaitAuthCallback(callbackKey: string, socketUrl?: string): Promise<{
    accessToken: string;
    refreshToken: string;
  }> {
    const base = socketUrl ?? PUBLIC_API_URL ?? "";
    const pollUrl = `${base}/v1/auth/callback/${encodeURIComponent(callbackKey)}`;
    const timeoutMs = 5 * 60 * 1000; // 5 minutes
    const intervalMs = 600;
    const deadline = Date.now() + timeoutMs;

    while (Date.now() < deadline) {
      const res = await fetch(pollUrl);
      if (res.status === 200) {
        const body = await res.json() as { data: { accessToken: string; refreshToken: string } };
        return body.data;
      }
      if (res.status !== 202) {
        throw new Error(`Unexpected status ${res.status} from auth callback endpoint`);
      }
      // 202 = still pending — wait and retry
      await new Promise((r) => setTimeout(r, intervalMs));
    }

    throw new Error("Timed out waiting for auth callback");
  },
};
