'use client';

import { useState, useEffect, useCallback, useRef } from 'react';
import { nanoid } from 'nanoid';
import {
  RoomSync,
  stateToParticipant,
  stateToWaypoint,
  type ParticipantState,
  type LocationState,
  type WaypointState,
  type RoomState,
} from '@/lib/sync';
import type { Participant, ParticipantLocation, Waypoint } from '@/types';

// Color palette for participants
const COLORS = [
  '#10b981', // emerald
  '#6366f1', // indigo
  '#f59e0b', // amber
  '#ef4444', // red
  '#8b5cf6', // violet
  '#ec4899', // pink
  '#14b8a6', // teal
  '#f97316', // orange
  '#84cc16', // lime
  '#06b6d4', // cyan
];

// Service worker room state caching
function saveRoomStateToSW(slug: string, state: { participants: Participant[]; waypoints: Waypoint[] }) {
  if (typeof navigator === 'undefined' || !navigator.serviceWorker?.controller) return;

  navigator.serviceWorker.controller.postMessage({
    type: 'SAVE_ROOM_STATE',
    slug,
    state,
  });
}

async function loadRoomStateFromSW(slug: string): Promise<{ participants: Participant[]; waypoints: Waypoint[] } | null> {
  if (typeof navigator === 'undefined' || !navigator.serviceWorker?.controller) return null;

  return new Promise((resolve) => {
    const timeout = setTimeout(() => resolve(null), 2000);

    const handler = (event: MessageEvent) => {
      if (event.data?.type === 'ROOM_STATE' && event.data.slug === slug) {
        clearTimeout(timeout);
        navigator.serviceWorker?.removeEventListener('message', handler);
        resolve(event.data.state || null);
      }
    };

    navigator.serviceWorker.addEventListener('message', handler);
    navigator.serviceWorker.controller!.postMessage({
      type: 'GET_ROOM_STATE',
      slug,
    });
  });
}

/**
 * Get or create a persistent participant ID for this browser/room combination.
 * Uses EncryptID DID when authenticated for cross-session persistence,
 * otherwise falls back to localStorage nanoid.
 */
function getOrCreateParticipantId(slug: string, encryptIdDid?: string | null): string {
  // If authenticated with EncryptID, use DID as stable identity
  if (encryptIdDid) {
    console.log('Using EncryptID DID as participant ID:', encryptIdDid.slice(0, 20) + '...');
    return encryptIdDid;
  }

  const storageKey = `rmaps_participant_id_${slug}`;

  try {
    let participantId = localStorage.getItem(storageKey);

    if (!participantId) {
      participantId = nanoid();
      localStorage.setItem(storageKey, participantId);
      console.log('Created new participant ID:', participantId);
    } else {
      console.log('Using existing participant ID:', participantId);
    }

    return participantId;
  } catch {
    // localStorage not available (SSR or private browsing)
    console.warn('localStorage not available, generating temporary participant ID');
    return nanoid();
  }
}

interface UseRoomOptions {
  slug: string;
  userName: string;
  userEmoji: string;
  /** EncryptID DID for persistent cross-session identity (optional) */
  encryptIdDid?: string | null;
  /** EncryptID JWT token for authenticated sync server access (optional) */
  authToken?: string | null;
}

interface UseRoomReturn {
  isConnected: boolean;
  isLoading: boolean;
  error: string | null;
  participants: Participant[];
  waypoints: Waypoint[];
  currentParticipantId: string | null;
  roomName: string;
  updateLocation: (location: ParticipantLocation) => void;
  updateIndoorPosition: (position: { level: number; x: number; y: number }) => void;
  clearLocation: () => void;
  setStatus: (status: Participant['status']) => void;
  addWaypoint: (waypoint: Omit<Waypoint, 'id' | 'createdAt' | 'createdBy'>) => void;
  removeWaypoint: (waypointId: string) => void;
  leave: () => void;
  setLocationRequestCallback: (callback: () => void) => void;
}

export function useRoom({ slug, userName, userEmoji, encryptIdDid, authToken }: UseRoomOptions): UseRoomReturn {
  const [isConnected, setIsConnected] = useState(false);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [participants, setParticipants] = useState<Participant[]>([]);
  const [waypoints, setWaypoints] = useState<Waypoint[]>([]);
  const [roomName, setRoomName] = useState(slug);

  const syncRef = useRef<RoomSync | null>(null);
  // Initialize with empty string - will be set properly on client side
  const participantIdRef = useRef<string>('');

  // Track if we've loaded cached state (to avoid overwriting with stale data)
  const hasSyncedRef = useRef(false);

  // Handle state updates from sync
  const handleStateChange = useCallback((state: RoomState) => {
    const newParticipants = Object.values(state.participants).map(stateToParticipant);
    const newWaypoints = state.waypoints.map(stateToWaypoint);

    setParticipants(newParticipants);
    setWaypoints(newWaypoints);
    setRoomName(state.name || slug);

    // Mark as synced and save to service worker for offline access
    hasSyncedRef.current = true;
    saveRoomStateToSW(slug, { participants: newParticipants, waypoints: newWaypoints });
  }, [slug]);

  // Handle connection changes
  const handleConnectionChange = useCallback((connected: boolean) => {
    setIsConnected(connected);
  }, []);

  // Load cached room state from service worker for offline fallback
  useEffect(() => {
    if (!slug) return;

    loadRoomStateFromSW(slug).then((cachedState) => {
      if (cachedState && !hasSyncedRef.current) {
        console.log('[useRoom] Loaded cached room state:', cachedState);
        setParticipants(cachedState.participants || []);
        setWaypoints(cachedState.waypoints || []);
      }
    });
  }, [slug]);

  // Initialize room connection
  useEffect(() => {
    if (!userName) {
      // No user yet - not loading, just waiting
      setIsLoading(false);
      return;
    }

    setIsLoading(true);
    setError(null);

    // Use persistent participant ID to avoid creating duplicate "ghost" participants
    const participantId = getOrCreateParticipantId(slug, encryptIdDid);
    participantIdRef.current = participantId;
    const color = COLORS[Math.floor(Math.random() * COLORS.length)];

    // Create sync instance (pass auth token for server-side access control)
    const sync = new RoomSync(
      slug,
      participantId,
      handleStateChange,
      handleConnectionChange,
      undefined,
      authToken
    );
    syncRef.current = sync;

    // Create participant state
    const participant: ParticipantState = {
      id: participantId,
      name: userName,
      emoji: userEmoji,
      color,
      joinedAt: new Date().toISOString(),
      lastSeen: new Date().toISOString(),
      status: 'online',
    };

    // Join room
    sync.join(participant);

    // Connect to sync server (if available)
    // For now, runs in local-only mode
    const syncUrl = process.env.NEXT_PUBLIC_SYNC_URL;
    sync.connect(syncUrl);

    setIsLoading(false);

    return () => {
      sync.leave();
      syncRef.current = null;
    };
  }, [slug, userName, userEmoji, handleStateChange, handleConnectionChange]);

  // Update location
  const updateLocation = useCallback((location: ParticipantLocation) => {
    if (!syncRef.current) return;

    const locationState: LocationState = {
      latitude: location.latitude,
      longitude: location.longitude,
      accuracy: location.accuracy,
      altitude: location.altitude,
      heading: location.heading,
      speed: location.speed,
      timestamp: location.timestamp.toISOString(),
      source: location.source,
      indoor: location.indoor,
    };

    syncRef.current.updateLocation(locationState);
  }, []);

  // Clear location (when user stops sharing)
  const clearLocation = useCallback(() => {
    if (!syncRef.current) return;
    syncRef.current.clearLocation();
  }, []);

  // Update indoor position (from c3nav map tap)
  const updateIndoorPosition = useCallback((position: { level: number; x: number; y: number }) => {
    if (!syncRef.current) return;
    syncRef.current.updateIndoorPosition(position);
  }, []);

  // Set status
  const setStatus = useCallback((status: Participant['status']) => {
    if (!syncRef.current) return;
    syncRef.current.updateStatus(status);
  }, []);

  // Add waypoint
  const addWaypoint = useCallback(
    (waypoint: Omit<Waypoint, 'id' | 'createdAt' | 'createdBy'>) => {
      if (!syncRef.current) return;

      const waypointState: WaypointState = {
        id: nanoid(),
        name: waypoint.name,
        emoji: waypoint.emoji,
        latitude: waypoint.location.latitude,
        longitude: waypoint.location.longitude,
        indoor: waypoint.location.indoor,
        createdBy: participantIdRef.current,
        createdAt: new Date().toISOString(),
        type: waypoint.type,
      };

      syncRef.current.addWaypoint(waypointState);
    },
    []
  );

  // Remove waypoint
  const removeWaypoint = useCallback((waypointId: string) => {
    if (!syncRef.current) return;
    syncRef.current.removeWaypoint(waypointId);
  }, []);

  // Leave room
  const leave = useCallback(() => {
    if (!syncRef.current) return;
    syncRef.current.leave();
    syncRef.current = null;
    setIsConnected(false);
  }, []);

  // Set location request callback (called when server requests location update)
  const setLocationRequestCallback = useCallback((callback: () => void) => {
    if (syncRef.current) {
      syncRef.current.setLocationRequestCallback(callback);
    }
  }, []);

  return {
    isConnected,
    isLoading,
    error,
    participants,
    waypoints,
    currentParticipantId: participantIdRef.current,
    roomName,
    updateLocation,
    updateIndoorPosition,
    clearLocation,
    setStatus,
    addWaypoint,
    removeWaypoint,
    leave,
    setLocationRequestCallback,
  };
}
