import React, { createContext, useContext, useState, useRef, useEffect } from 'react';

interface VaultContextType {
  isLocked: boolean;
  isAuthenticated: boolean;
  communityId: string | null;
  username: string | null;
  userId: string | null;
  role: string | null;
  unlock: (key: Uint8Array, communityId: string, username: string, userId: string, role?: string) => void;
  lock: () => void;
  logout: () => void;
  decrypt: (data: Uint8Array) => Promise<string>;
  encrypt: (text: string) => Promise<Uint8Array>;
  hash: (text: string) => Promise<string>;
}

const VaultContext = createContext<VaultContextType | undefined>(undefined);

const makeWorker = () =>
  new Worker(new URL('../workers/crypto.worker.ts', import.meta.url), { type: 'module' });

export const VaultProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
  const [isLocked, setIsLocked] = useState(true);
  const [isAuthenticated, setIsAuthenticated] = useState(false);
  const [communityId, setCommunityId] = useState<string | null>(null);
  const [username, setUsername] = useState<string | null>(null);
  const [userId, setUserId] = useState<string | null>(null);
  const [role, setRole] = useState<string | null>(null);
  const workerRef = useRef<Worker | null>(null);

  useEffect(() => {
    workerRef.current = makeWorker();
    return () => {
      workerRef.current?.terminate();
    };
  }, []);

  const unlock = (key: Uint8Array, community: string, user: string, uid: string, userRole = 'member') => {
    workerRef.current?.postMessage({ type: 'SET_KEY', payload: key });
    setIsLocked(false);
    setIsAuthenticated(true);
    setCommunityId(community);
    setUsername(user);
    setUserId(uid);
    setRole(userRole);
  };

  const lock = () => {
    workerRef.current?.terminate();
    workerRef.current = makeWorker();
    setIsLocked(true);
    setIsAuthenticated(false);
    setCommunityId(null);
    setUsername(null);
    setUserId(null);
    setRole(null);
  };

  const logout = () => {
    localStorage.removeItem('auth_token');
    lock();
  };

  const decrypt = (data: Uint8Array): Promise<string> => {
    return new Promise((resolve, reject) => {
      if (isLocked || !workerRef.current) return reject('Vault is locked');

      const requestId = Math.random().toString(36).slice(2);

      const handleMessage = (e: MessageEvent) => {
        if (e.data.id === requestId) {
          workerRef.current?.removeEventListener('message', handleMessage);
          if (e.data.type === 'DECRYPTED') {
            resolve(e.data.payload);
          } else if (e.data.type === 'ERROR') {
            reject(e.data.message);
          }
        }
      };

      workerRef.current.addEventListener('message', handleMessage);
      workerRef.current.postMessage({ type: 'DECRYPT', payload: data, id: requestId });
    });
  };

  const encrypt = (text: string): Promise<Uint8Array> => {
    return new Promise((resolve, reject) => {
      if (isLocked || !workerRef.current) return reject('Vault is locked');

      const requestId = Math.random().toString(36).slice(2);

      const handleMessage = (e: MessageEvent) => {
        if (e.data.id === requestId) {
          workerRef.current?.removeEventListener('message', handleMessage);
          if (e.data.type === 'ENCRYPTED') {
            resolve(e.data.payload);
          } else if (e.data.type === 'ERROR') {
            reject(e.data.message);
          }
        }
      };

      workerRef.current.addEventListener('message', handleMessage);
      workerRef.current.postMessage({ type: 'ENCRYPT', payload: text, id: requestId });
    });
  };

  const hash = (text: string): Promise<string> => {
    return new Promise((resolve, reject) => {
      if (isLocked || !workerRef.current) return reject('Vault is locked');

      const requestId = Math.random().toString(36).slice(2);

      const handleMessage = (e: MessageEvent) => {
        if (e.data.id === requestId) {
          workerRef.current?.removeEventListener('message', handleMessage);
          if (e.data.type === 'HASHED') {
            resolve(e.data.payload);
          } else if (e.data.type === 'ERROR') {
            reject(e.data.message);
          }
        }
      };

      workerRef.current.addEventListener('message', handleMessage);
      workerRef.current.postMessage({ type: 'BLIND_INDEX', payload: text, id: requestId });
    });
  };

  return (
    <VaultContext.Provider value={{ isLocked, isAuthenticated, communityId, username, userId, role, unlock, lock, logout, decrypt, encrypt, hash }}>
      {children}
    </VaultContext.Provider>
  );
};

export const useVault = () => {
  const context = useContext(VaultContext);
  if (!context) throw new Error('useVault must be used within a VaultProvider');
  return context;
};
