/**
 * Client-Side Experiment Tracking
 *
 * Utilities for tracking A/B experiment views and conversions
 * on the frontend. Works with the experiments service API.
 */

import type { ExperimentType } from "@/lib/types/experiments.types";

// Session ID for deterministic variant assignment
function getSessionId(): string {
  if (typeof window === "undefined") return "ssr-no-session";

  let sessionId = sessionStorage.getItem("mawidi_experiment_session");
  if (!sessionId) {
    sessionId = `sess_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`;
    sessionStorage.setItem("mawidi_experiment_session", sessionId);
  }
  return sessionId;
}

// User ID if available
function getUserId(): string | undefined {
  if (typeof window === "undefined") return undefined;

  // Try to get user ID from localStorage or cookies
  // This should be set when user logs in
  const userData = localStorage.getItem("mawidi_user");
  if (userData) {
    try {
      const parsed = JSON.parse(userData);
      return parsed.id;
    } catch {
      return undefined;
    }
  }
  return undefined;
}

interface VariantAssignment {
  experimentId: string;
  variantId: string;
  variantName: string;
}

// Cache variant assignments to avoid duplicate API calls
// Limited size to prevent memory leaks
const MAX_ASSIGNMENT_CACHE_SIZE = 10000;
const assignmentCache = new Map<string, VariantAssignment>();

/**
 * Add to assignment cache with LRU eviction
 */
function addToAssignmentCache(key: string, value: VariantAssignment): void {
  // Evict oldest entry if at capacity
  if (assignmentCache.size >= MAX_ASSIGNMENT_CACHE_SIZE) {
    const oldestKey = assignmentCache.keys().next().value;
    if (oldestKey) {
      assignmentCache.delete(oldestKey);
    }
  }
  assignmentCache.set(key, value);
}

/**
 * Get active experiments for a specific type
 */
export async function getActiveExperiments(type: ExperimentType): Promise<
  Array<{
    id: string;
    name: string;
    variants: Array<{ id: string; name: string }>;
  }>
> {
  try {
    const response = await fetch(`/api/experiments/active?type=${type}`);
    if (!response.ok) return [];

    const data = await response.json();
    return data.experiments || [];
  } catch {
    return [];
  }
}

/**
 * Get assigned variant for an experiment
 * Uses deterministic assignment based on session ID
 */
export async function getVariantAssignment(
  experimentId: string,
): Promise<VariantAssignment | null> {
  // Check cache first
  if (assignmentCache.has(experimentId)) {
    return assignmentCache.get(experimentId)!;
  }

  try {
    const sessionId = getSessionId();
    const userId = getUserId();

    const response = await fetch("/api/experiments/assign", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ experimentId, sessionId, userId }),
    });

    if (!response.ok) return null;

    const data = await response.json();
    if (data.assignment) {
      addToAssignmentCache(experimentId, data.assignment);
      return data.assignment;
    }

    return null;
  } catch {
    return null;
  }
}

/**
 * Track experiment view (impression)
 */
export async function trackExperimentView(
  experimentId: string,
  variantId: string,
): Promise<void> {
  if (typeof window === "undefined") return;

  try {
    const sessionId = getSessionId();
    const userId = getUserId();

    await fetch("/api/experiments/track", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        experimentId,
        variantId,
        eventType: "view",
        sessionId,
        userId,
      }),
    });

    // Also track in PostHog if available
    if (typeof window !== "undefined" && "posthog" in window) {
      (
        window as {
          posthog?: {
            capture: (name: string, props: Record<string, unknown>) => void;
          };
        }
      ).posthog?.capture("experiment_view", {
        experiment_id: experimentId,
        variant_id: variantId,
        session_id: sessionId,
      });
    }
  } catch {
    // Silent fail - don't block user experience
  }
}

/**
 * Track experiment conversion
 */
export async function trackExperimentConversion(
  experimentId: string,
  variantId: string,
  metadata?: Record<string, unknown>,
): Promise<void> {
  if (typeof window === "undefined") return;

  try {
    const sessionId = getSessionId();
    const userId = getUserId();

    await fetch("/api/experiments/track", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        experimentId,
        variantId,
        eventType: "conversion",
        sessionId,
        userId,
        metadata,
      }),
    });

    // Also track in PostHog if available
    if (typeof window !== "undefined" && "posthog" in window) {
      (
        window as {
          posthog?: {
            capture: (name: string, props: Record<string, unknown>) => void;
          };
        }
      ).posthog?.capture("experiment_conversion", {
        experiment_id: experimentId,
        variant_id: variantId,
        session_id: sessionId,
        ...metadata,
      });
    }
  } catch {
    // Silent fail - don't block user experience
  }
}

/**
 * Hook to use experiments in React components
 * Returns the assigned variant for a specific experiment type
 */
export function useExperiment(_experimentId: string): {
  variant: VariantAssignment | null;
  isLoading: boolean;
  trackView: () => void;
  trackConversion: (metadata?: Record<string, unknown>) => void;
} {
  // This is a stub - would need to implement with useState/useEffect
  // For SSR safety, return a stub implementation
  return {
    variant: null,
    isLoading: false,
    trackView: () => {
      // Stub
    },
    trackConversion: () => {
      // Stub
    },
  };
}

/**
 * Deterministic variant selector based on session
 * Can be used for client-side variant assignment without API calls
 */
export function selectVariant<T extends { id: string; trafficPercent: number }>(
  variants: T[],
  sessionId?: string,
): T | null {
  if (!variants.length) return null;

  const sid = sessionId || getSessionId();

  // Simple hash function
  let hash = 0;
  for (let i = 0; i < sid.length; i++) {
    hash = (hash * 31 + sid.charCodeAt(i)) | 0;
  }
  const bucket = Math.abs(hash % 100);

  // Find variant based on traffic split
  let cumulative = 0;
  for (const variant of variants) {
    cumulative += variant.trafficPercent;
    if (bucket < cumulative) {
      return variant;
    }
  }

  // Fallback to first variant
  return variants[0];
}

/**
 * Check if user is in experiment (for conditional rendering)
 */
export function isInExperiment(experimentId: string): boolean {
  return assignmentCache.has(experimentId);
}

/**
 * Get all current experiment assignments for debugging
 */
export function getExperimentAssignments(): Record<string, VariantAssignment> {
  return Object.fromEntries(assignmentCache);
}

/**
 * Clear experiment assignments (useful for testing)
 */
export function clearExperimentAssignments(): void {
  assignmentCache.clear();
  if (typeof window !== "undefined") {
    sessionStorage.removeItem("mawidi_experiment_session");
  }
}
