import React, { createContext, useContext, useState, useEffect, useMemo } from 'react';
import { ethers, Contract } from 'ethers';
import type { 
  SoulStreamContextType, 
  SoulStreamProviderProps, 
  SoulStreamConfig,
  ReputationData,
  RewardsData,
  ChainConfig
} from '../types';

// Create context with null as initial value, but with type
const SoulStreamContext = createContext<SoulStreamContextType | null>(null);

/**
 * Custom hook to use the SoulStream context
 * @returns {SoulStreamContextType} SoulStream context values
 */
export const useSoulStream = (): SoulStreamContextType => {
  const context = useContext(SoulStreamContext);
  if (!context) {
    throw new Error('useSoulStream must be used within a SoulStreamProvider');
  }
  return context;
};

/**
 * SoulStream Provider Component
 */
export const SoulStreamProvider: React.FC<SoulStreamProviderProps> = ({ 
  children, 
  config = {
    rpcUrl: process.env.NEXT_PUBLIC_RPC_URL || 'https://rpc.example.com',
    contractAddress: process.env.NEXT_PUBLIC_SOULSTREAM_CONTRACT || '0x0000000000000000000000000000000000000000',
    contractABI: [], // Default empty ABI
    chainA: {
      chainId: process.env.NEXT_PUBLIC_CHAIN_A_ID || '31337',
      rpcUrl: process.env.NEXT_PUBLIC_CHAIN_A_RPC_URL || 'http://localhost:8545',
      registryAddress: process.env.NEXT_PUBLIC_CHAIN_A_REGISTRY_ADDRESS || '0x0000000000000000000000000000000000000000',
      bridgeAdapterAddress: process.env.NEXT_PUBLIC_CHAIN_A_BRIDGE_ADAPTER_ADDRESS || '0x0000000000000000000000000000000000000000'
    },
    chainB: {
      chainId: process.env.NEXT_PUBLIC_CHAIN_B_ID || '31338',
      rpcUrl: process.env.NEXT_PUBLIC_CHAIN_B_RPC_URL || 'http://localhost:8546',
      registryAddress: process.env.NEXT_PUBLIC_CHAIN_B_REGISTRY_ADDRESS || '0x0000000000000000000000000000000000000000',
      bridgeAdapterAddress: process.env.NEXT_PUBLIC_CHAIN_B_BRIDGE_ADAPTER_ADDRESS || '0x0000000000000000000000000000000000000000'
    },
    privateKey: process.env.PRIVATE_KEY
  }
}) => {
  const [provider, setProvider] = useState<ethers.JsonRpcProvider | null>(null);
  const [contract, setContract] = useState<Contract | null>(null);
  const [isInitialized, setIsInitialized] = useState<boolean>(false);
  const [error, setError] = useState<string | null>(null);
  const [currentChainId, setCurrentChainId] = useState<string | null>(null);
  
  // Store chain configurations
  const chains = useMemo(() => ({
    chainA: config.chainA || null,
    chainB: config.chainB || null
  }), [config.chainA, config.chainB]);

  // Initialize the provider and contract
  useEffect(() => {
    const init = async () => {
      try {
        // Start with Chain A by default
        const defaultChain = config.chainA;
        if (!defaultChain) {
          throw new Error('No chain configuration provided');
        }
        
        // Create provider for the default chain
        const ethersProvider = new ethers.JsonRpcProvider(defaultChain.rpcUrl);
        setProvider(ethersProvider);
        setCurrentChainId(defaultChain.chainId);
        
        // Create contract instance using the registry address
        const contractInstance = new ethers.Contract(
          defaultChain.registryAddress,
          config.contractABI,
          ethersProvider
        );
        setContract(contractInstance);
        
        setIsInitialized(true);
        setError(null);
      } catch (err) {
        console.error('Failed to initialize SoulStream:', err);
        setError('Failed to initialize SoulStream');
        setIsInitialized(false);
      }
    };
    
    init();
  }, [config]);

  /**
   * Switch between different chains
   * @param {string} chainId - The chain ID to switch to
   * @returns {Promise<boolean>} - Success status
   */
  const switchChain = async (chainId: string): Promise<boolean> => {
    try {
      const targetChain = 
        chains.chainA?.chainId === chainId ? chains.chainA :
        chains.chainB?.chainId === chainId ? chains.chainB :
        null;
      
      if (!targetChain) {
        throw new Error(`Chain with ID ${chainId} not configured`);
      }
      
      // Create a new provider for the target chain
      const ethersProvider = new ethers.JsonRpcProvider(targetChain.rpcUrl);
      setProvider(ethersProvider);
      
      // Create a new contract instance
      const contractInstance = new ethers.Contract(
        targetChain.registryAddress,
        config.contractABI,
        ethersProvider
      );
      setContract(contractInstance);
      
      setCurrentChainId(chainId);
      return true;
    } catch (err) {
      console.error('Failed to switch chain:', err);
      return false;
    }
  };

  /**
   * Get reputation score for a wallet address
   * @param {string} walletAddress - Ethereum wallet address
   * @returns {Promise<ReputationData>} - Reputation data
   */
  const getReputationScore = async (walletAddress: string): Promise<ReputationData> => {
    if (!isInitialized || !contract) {
      throw new Error('SoulStream not initialized');
    }
    
    try {
      // This is where you would call your contract method
      // For example: const score = await contract.getReputationScore(walletAddress);
      
      // Mock data for now
      return {
        score: 85,
        trust: 90,
        activity: 80,
        engagement: 85
      };
    } catch (err) {
      console.error('Failed to get reputation score:', err);
      throw new Error('Failed to get reputation score');
    }
  };

  /**
   * Get rewards for a wallet address
   * @param {string} walletAddress - Ethereum wallet address
   * @returns {Promise<RewardsData>} - Rewards data
   */
  const getRewards = async (walletAddress: string): Promise<RewardsData> => {
    if (!isInitialized || !contract) {
      throw new Error('SoulStream not initialized');
    }
    
    try {
      // This is where you would call your contract method
      // For example: const rewards = await contract.getRewards(walletAddress);
      
      // Mock data for now
      return {
        unclaimed: [
          { id: '1', name: 'Community Contribution', amount: '100 SOUL' },
          { id: '2', name: 'Active Participation', amount: '50 SOUL' }
        ],
        claimed: [
          { 
            id: '3', 
            name: 'Early Adopter', 
            amount: '200 SOUL',
            claimedAt: Date.now() - 86400000 // 1 day ago
          }
        ]
      };
    } catch (err) {
      console.error('Failed to get rewards:', err);
      throw new Error('Failed to get rewards');
    }
  };

  /**
   * Claim a reward
   * @param {string} walletAddress - Ethereum wallet address
   * @param {string} rewardId - ID of the reward to claim
   * @returns {Promise<boolean>} - Success status
   */
  const claimReward = async (walletAddress: string, rewardId: string): Promise<boolean> => {
    if (!isInitialized || !contract) {
      throw new Error('SoulStream not initialized');
    }
    
    try {
      // This is where you would call your contract method
      // For example: await contract.claimReward(rewardId, { from: walletAddress });
      
      // Mock success for now
      return true;
    } catch (err) {
      console.error('Failed to claim reward:', err);
      throw new Error('Failed to claim reward');
    }
  };
  
  // Create the context value
  const contextValue = useMemo<SoulStreamContextType>(() => ({
    isInitialized,
    error,
    getReputationScore,
    getRewards,
    claimReward,
    chains,
    switchChain,
    currentChainId
  }), [isInitialized, error, chains, currentChainId]);
  
  return (
    <SoulStreamContext.Provider value={contextValue}>
      {children}
    </SoulStreamContext.Provider>
  );
};

// Export the context
export default SoulStreamContext;