export type UserRole = "ADMIN" | "FAMILY";

export type AuthUser = {
  id: string;
  username: string;
  displayName: string | null;
  profileImageUrl: string | null;
  workspaceId: string | null;
  currentProjectId: string | null;
  role: UserRole;
  createdAt: string;
};

export type SessionPayload = {
  accessToken: string;
  refreshToken: string;
  user: AuthUser;
};

export const ACCESS_TOKEN_KEY = "ljp_access_token";
export const REFRESH_TOKEN_KEY = "ljp_refresh_token";
export const USER_KEY = "ljp_user";
export const CURRENT_PROJECT_KEY = "ljp_current_project";
export const PROJECT_CHANGED_EVENT = "ljp_project_changed";

export function getApiUrl() {
  const configuredApiUrl = process.env.NEXT_PUBLIC_API_URL?.replace(/\/$/, "");

  if (configuredApiUrl) {
    return configuredApiUrl;
  }

  if (typeof window !== "undefined") {
    const { hostname, protocol } = window.location;

    if (hostname && hostname !== "localhost" && hostname !== "127.0.0.1") {
      return `${protocol}//${hostname}:3001`;
    }
  }

  return "http://localhost:3001";
}

export function loadSession() {
  if (typeof window === "undefined") {
    return null;
  }

  const accessToken = localStorage.getItem(ACCESS_TOKEN_KEY);
  const refreshToken = localStorage.getItem(REFRESH_TOKEN_KEY);
  const rawUser = localStorage.getItem(USER_KEY);

  if (!accessToken || !refreshToken || !rawUser) {
    return null;
  }

  try {
    const parsedUser = JSON.parse(rawUser) as Partial<AuthUser>;
    const persistedProjectId = localStorage.getItem(CURRENT_PROJECT_KEY);
    const user: AuthUser = {
      id: parsedUser.id ?? "",
      username: parsedUser.username ?? "",
      displayName: parsedUser.displayName ?? null,
      profileImageUrl: parsedUser.profileImageUrl ?? null,
      workspaceId: parsedUser.workspaceId ?? null,
      currentProjectId: persistedProjectId ?? parsedUser.currentProjectId ?? null,
      role: (parsedUser.role as UserRole | undefined) ?? "FAMILY",
      createdAt: parsedUser.createdAt ?? new Date().toISOString(),
    };

    if (!user.id || !user.username) {
      clearSession();
      return null;
    }

    return { accessToken, refreshToken, user };
  } catch {
    clearSession();
    return null;
  }
}

export function saveSession(payload: SessionPayload) {
  if (typeof window === "undefined") {
    return;
  }

  const sessionUser: AuthUser = {
    ...payload.user,
    workspaceId: payload.user.workspaceId ?? null,
    currentProjectId: payload.user.currentProjectId ?? null,
  };

  localStorage.setItem(ACCESS_TOKEN_KEY, payload.accessToken);
  localStorage.setItem(REFRESH_TOKEN_KEY, payload.refreshToken);
  localStorage.setItem(USER_KEY, JSON.stringify(sessionUser));

  if (sessionUser.currentProjectId) {
    localStorage.setItem(CURRENT_PROJECT_KEY, sessionUser.currentProjectId);
  } else {
    localStorage.removeItem(CURRENT_PROJECT_KEY);
  }
}

export function clearSession() {
  if (typeof window === "undefined") {
    return;
  }

  localStorage.removeItem(ACCESS_TOKEN_KEY);
  localStorage.removeItem(REFRESH_TOKEN_KEY);
  localStorage.removeItem(USER_KEY);
  localStorage.removeItem(CURRENT_PROJECT_KEY);
}

export async function logoutSession(apiUrl: string) {
  try {
    await authenticatedFetch(apiUrl, "/auth/logout", { method: "POST" });
  } catch {
    // no-op
  }

  clearSession();
}

export function getCurrentProjectId() {
  if (typeof window === "undefined") {
    return null;
  }

  const session = loadSession();
  return session?.user.currentProjectId ?? null;
}

export function setCurrentProjectId(projectId: string | null) {
  if (typeof window === "undefined") {
    return;
  }

  const session = loadSession();
  if (!session) {
    return;
  }

  const nextUser: AuthUser = {
    ...session.user,
    currentProjectId: projectId,
  };

  localStorage.setItem(USER_KEY, JSON.stringify(nextUser));
  if (projectId) {
    localStorage.setItem(CURRENT_PROJECT_KEY, projectId);
  } else {
    localStorage.removeItem(CURRENT_PROJECT_KEY);
  }

  if (typeof window !== "undefined") {
    window.dispatchEvent(
      new CustomEvent(PROJECT_CHANGED_EVENT, {
        detail: { projectId },
      }),
    );
  }
}

async function requestTokenRefresh(apiUrl: string, refreshToken: string) {
  const response = await fetch(`${apiUrl}/auth/refresh`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ refreshToken }),
  });

  const payload = (await response.json()) as SessionPayload | { message?: string };

  if (!response.ok || !("accessToken" in payload) || !("refreshToken" in payload)) {
    throw new Error((payload as { message?: string }).message ?? "Session expiree");
  }

  saveSession(payload);
  return payload;
}

export async function authenticatedFetch(
  apiUrl: string,
  path: string,
  init: RequestInit = {},
): Promise<Response> {
  const session = loadSession();

  if (!session) {
    throw new Error("Session manquante");
  }

  const headers = new Headers(init.headers ?? {});
  headers.set("Authorization", `Bearer ${session.accessToken}`);

  const firstResponse = await fetch(`${apiUrl}${path}`, {
    ...init,
    headers,
  });

  if (firstResponse.status !== 401) {
    return firstResponse;
  }

  const refreshedSession = await requestTokenRefresh(apiUrl, session.refreshToken);

  const retryHeaders = new Headers(init.headers ?? {});
  retryHeaders.set("Authorization", `Bearer ${refreshedSession.accessToken}`);

  return fetch(`${apiUrl}${path}`, {
    ...init,
    headers: retryHeaders,
  });
}
