import React, {useEffect, useState} from 'react'
import {useDelegatedActions, usePrivy, useWallets, WalletWithMetadata} from '@privy-io/react-auth'
import {Account} from '../../../generated/ManagingApi'
import canonicalize from 'canonicalize' // Support JSON canonicalization
import elliptic from 'elliptic' // Elliptic curve cryptography for browsers
import * as base64 from 'base64-js' // Base64 encoding/decoding
import Modal from '../../../components/mollecules/Modal/Modal'

// Replace with your app configuration - these should come from environment variables in a real app
const PRIVY_APP_ID = 'insert-your-app-id'
const PRIVY_APP_SECRET = 'insert-your-app-secret' // Don't hardcode in production!
const PRIVY_AUTHORIZATION_KEY = 'wallet-auth:insert-your-private-key-here' // Don't hardcode in production!

// Initialize elliptic curve - P-256 is the curve used by Privy (NIST P-256)
const EC = new elliptic.ec('p256')

interface PrivyDelegationModalProps {
  isOpen: boolean
  onClose: () => void
  account: Account
}

// Use Web Crypto API to hash the message with SHA-256
async function sha256Hash(message: Uint8Array): Promise<Uint8Array> {
  const hashBuffer = await crypto.subtle.digest('SHA-256', message)
  return new Uint8Array(hashBuffer)
}

async function generateAuthorizationSignature({url, body}: {url: string; body: object}) {
  try {
    // First, canonicalize the request body - this is crucial for consistent signatures
    const canonicalizedBody = JSON.parse(canonicalize(body) as string);
    
    // Create the payload object that follows Privy's specification
    const payload = {
      version: 1,
      method: 'POST',
      url,
      body: canonicalizedBody, // Use the canonicalized body here
      headers: {
        'privy-app-id': PRIVY_APP_ID
      }
    };

    // JSON-canonicalize the payload to ensure deterministic serialization
    const serializedPayload = canonicalize(payload) as string;
    
    console.log('Canonicalized payload:', serializedPayload);
    
    // Extract private key from the authorization key by removing the prefix
    const privateKeyBase64 = PRIVY_AUTHORIZATION_KEY.replace('wallet-auth:', '');
    
    // Decode the base64 private key to get the raw bytes
    const privateKeyBytes = base64.toByteArray(privateKeyBase64);
    
    // Import the private key into elliptic
    const privateKey = EC.keyFromPrivate(privateKeyBytes);
    
    // Convert the serialized payload to UTF-8 bytes
    const messageBytes = new TextEncoder().encode(serializedPayload);
    
    // Hash the message using SHA-256 with Web Crypto API
    const messageHash = await sha256Hash(messageBytes);
    
    // Sign the hash with the private key - use Uint8Array instead of Buffer
    // Using the array directly instead of creating a Buffer
    const signature = privateKey.sign(Array.from(messageHash));
    
    // Convert signature to the DER format and then to base64
    const signatureDer = signature.toDER();
    const signatureBase64 = base64.fromByteArray(new Uint8Array(signatureDer));
    
    return {
      payload: serializedPayload,
      signature: signatureBase64,
      body: canonicalizedBody, // Return the canonicalized body
      canonicalizedBodyString: canonicalize(canonicalizedBody) as string // Return the string form too
    };
  } catch (err) {
    console.error('Error generating signature:', err);
    throw err;
  }
}

// Function to send the actual request with the generated signature
async function sendPrivyRequest(url: string, body: any, signature: string): Promise<any> {
  try {
    // Create basic auth token for Privy API
    const authToken = btoa(`${PRIVY_APP_ID}:${PRIVY_APP_SECRET}`);
    
    // Set up the request headers
    const headers = {
      'Authorization': `Basic ${authToken}`,
      'privy-app-id': PRIVY_APP_ID,
      'privy-authorization-signature': signature,
      'Content-Type': 'application/json'
    };
    
    console.log('Sending request with headers:', headers);
    console.log('Body:', body);
    
    // Make the API call
    const response = await fetch(url, {
      method: 'POST',
      headers: headers,
      body: body // Use the exact same canonicalized body string
    });
    
    // Get the response body
    const responseText = await response.text();
    
    // Try to parse as JSON if possible
    try {
      const responseJson = JSON.parse(responseText);
      
      if (!response.ok) {
        throw new Error(`API error (${response.status}): ${JSON.stringify(responseJson)}`);
      }
      
      return responseJson;
    } catch (parseError) {
      // If not valid JSON, return as text
      if (!response.ok) {
        throw new Error(`API error (${response.status}): ${responseText}`);
      }
      
      return { text: responseText };
    }
  } catch (error) {
    console.error('Error sending Privy request:', error);
    throw error;
  }
}

const PrivyDelegationModal: React.FC<PrivyDelegationModalProps> = ({
  isOpen,
  onClose,
  account,
}) => {
  const [isDelegated, setIsDelegated] = useState<boolean>(false)
  const [isLoading, setIsLoading] = useState<boolean>(true)
  const [error, setError] = useState<string | null>(null)
  const [signatureResult, setSignatureResult] = useState<{
    payload: string, 
    signature: string,
    body?: any,
    canonicalizedBodyString?: string
  } | null>(null)
  const [isSigningMessage, setIsSigningMessage] = useState<boolean>(false)
  const [isSendingRequest, setIsSendingRequest] = useState<boolean>(false)
  const [messageToSign, setMessageToSign] = useState<string>('Hello, Ethereum')
  const [apiResponse, setApiResponse] = useState<any>(null)

  const { delegateWallet, revokeWallets } = useDelegatedActions()
  const { user } = usePrivy()
  const { ready, wallets } = useWallets()

  // Find the embedded wallet to delegate from the array of the user's wallets
  const walletToDelegate = wallets.find(
    (wallet) => wallet.walletClientType === 'privy' && wallet.address === account.key
  )

  useEffect(() => {
    if (user && ready) {
      checkDelegationStatus()
    }
  }, [user, ready])

  const checkDelegationStatus = () => {
    setIsLoading(true)
    setError(null)

    try {
      // Check if the wallet is already delegated by inspecting the user's linked accounts
      const delegatedWallet = user?.linkedAccounts.find(
        (linkedAccount): linkedAccount is WalletWithMetadata => 
          linkedAccount.type === 'wallet' && 
          linkedAccount.delegated &&
          linkedAccount.address.toLowerCase() === account.key?.toLowerCase()
      )
      
      setIsDelegated(!!delegatedWallet)
    } catch (err: any) {
      console.error('Error checking delegation status:', err)
      setError(`Failed to check delegation status: ${err.message}`)
    } finally {
      setIsLoading(false)
    }
  }

  const handleRequestDelegation = async () => {
    try {
      setIsLoading(true)
      setError(null)

      if (!walletToDelegate) {
        throw new Error('No embedded wallet found to delegate')
      }

      // Fixes the linter error by using the correct parameter type
      await delegateWallet({
        address: walletToDelegate.address,
        chainType: 'ethereum'
      })
      
      setIsDelegated(true)
    } catch (err: any) {
      console.error('Error requesting delegation:', err)
      setError(`Failed to request delegation: ${err.message}`)
    } finally {
      setIsLoading(false)
    }
  }

  const handleRevokeDelegation = async () => {
    try {
      setIsLoading(true)
      setError(null)

      if (!walletToDelegate) {
        throw new Error('No embedded wallet found to revoke delegation')
      }

      // Fix for linter error - revokeWallets doesn't take parameters
      await revokeWallets()
      
      setIsDelegated(false)
    } catch (err: any) {
      console.error('Error revoking delegation:', err)
      setError(`Failed to revoke delegation: ${err.message}`)
    } finally {
      setIsLoading(false)
    }
  }

  const handleSignMessage = async () => {
    try {
      setIsSigningMessage(true)
      setSignatureResult(null)
      setApiResponse(null)
      setError(null)

      if (!walletToDelegate) {
        throw new Error('No embedded wallet found to sign with')
      }

      // Prepare the request body
      const body = {
        address: walletToDelegate.address,
        chain_type: 'ethereum',
        method: 'personal_sign',
        params: {
          message: messageToSign,
          encoding: 'utf-8'
        }
      }

      // Generate the signature using browser crypto
      const result = await generateAuthorizationSignature({
        url: 'https://auth.privy.io/api/v1/wallets/rpc',
        body
      })

      setSignatureResult(result)

      // For debugging in the console
      console.log("Generated Authorization Signature:", result.signature);
      console.log("Payload:", result.payload);
      console.log("Body:", result.body);
      console.log("Canonicalized Body String:", result.canonicalizedBodyString);
      
      // Create a curl command that could be used to make the request
      // Note: We're using the canonicalized body from the result to ensure it matches what was signed
      const curlCommand = `curl --request POST https://auth.privy.io/api/v1/wallets/rpc \\
-u "${PRIVY_APP_ID}:YOUR_APP_SECRET" \\
-H "privy-app-id: ${PRIVY_APP_ID}" \\
-H "privy-authorization-signature: ${result.signature}" \\
-H 'Content-Type: application/json' \\
-d '${result.canonicalizedBodyString}'`;
      
      console.log("Curl command for testing:");
      console.log(curlCommand);
      
    } catch (err: any) {
      console.error('Error signing message:', err)
      setError(`Failed to sign message: ${err.message}`)
    } finally {
      setIsSigningMessage(false)
    }
  }

  const handleSendRequest = async () => {
    if (!signatureResult?.canonicalizedBodyString || !signatureResult?.signature) {
      setError('No signature available. Please generate a signature first.');
      return;
    }

    try {
      setIsSendingRequest(true);
      setApiResponse(null);
      setError(null);

      // Send the actual request using the same canonicalized body that was used for signature
      const response = await sendPrivyRequest(
        'https://auth.privy.io/api/v1/wallets/rpc',
        signatureResult.canonicalizedBodyString,
        signatureResult.signature
      );

      setApiResponse(response);
      console.log('API Response:', response);
    } catch (err: any) {
      console.error('Error sending request:', err);
      setError(`Failed to send request: ${err.message}`);
    } finally {
      setIsSendingRequest(false);
    }
  };

  // Modal content to be passed to the Modal component
  const modalContent = (
    <>
      {isLoading ? (
        <div className="text-center py-4">
          <span className="loading loading-spinner loading-md"></span>
          <p>Checking delegation status...</p>
        </div>
      ) : error ? (
        <div className="alert alert-error mb-4">
          <p>{error}</p>
        </div>
      ) : (
        <>
          <div className="mb-4">
            <p className="mb-2">
              <strong>Wallet Address:</strong> {account.key}
            </p>
            <p className="mb-2">
              <strong>Delegation Status:</strong>{' '}
              <span className={isDelegated ? 'text-success' : 'text-error'}>
                {isDelegated ? 'Enabled' : 'Disabled'}
              </span>
            </p>
          </div>

          <div className="flex justify-center mb-4">
            {isDelegated ? (
              <button
                onClick={handleRevokeDelegation}
                className="btn btn-error"
                disabled={isLoading}
              >
                Revoke Delegation
              </button>
            ) : (
              <button
                onClick={handleRequestDelegation}
                className="btn btn-primary"
                disabled={isLoading}
              >
                Enable Delegation
              </button>
            )}
          </div>

          <div className="divider">Message Signing</div>
          
          <div className="mb-4">
            <label className="form-control w-full">
              <div className="label">
                <span className="label-text">Message to sign</span>
              </div>
              <input
                type="text"
                value={messageToSign}
                onChange={(e) => setMessageToSign(e.target.value)}
                className="input input-bordered w-full"
                disabled={isSigningMessage}
              />
            </label>
          </div>

          <div className="flex flex-col gap-2 mb-4">
            <button
              onClick={handleSignMessage}
              className="btn btn-secondary"
              disabled={isSigningMessage || !messageToSign}
            >
              {isSigningMessage ? (
                <>
                  <span className="loading loading-spinner loading-sm"></span>
                  Generating Signature...
                </>
              ) : (
                'Generate Signature'
              )}
            </button>

            {signatureResult && (
              <button
                onClick={handleSendRequest}
                className="btn btn-primary"
                disabled={isSendingRequest || !signatureResult}
              >
                {isSendingRequest ? (
                  <>
                    <span className="loading loading-spinner loading-sm"></span>
                    Sending Request...
                  </>
                ) : (
                  'Send Request Using Signature'
                )}
              </button>
            )}
          </div>

          {signatureResult && (
            <div className="mb-4">
              <div className="label">
                <span className="label-text font-bold">Generated Signature:</span>
              </div>
              <div className="bg-gray-100 p-2 rounded overflow-x-auto">
                <pre className="text-xs break-all whitespace-pre-wrap">{signatureResult.signature}</pre>
              </div>
            </div>
          )}

          {apiResponse && (
            <div className="mb-4">
              <div className="label">
                <span className="label-text font-bold">API Response:</span>
              </div>
              <div className="bg-gray-100 p-2 rounded overflow-x-auto">
                <pre className="text-xs break-all whitespace-pre-wrap">
                  {JSON.stringify(apiResponse, null, 2)}
                </pre>
              </div>
            </div>
          )}
          
          <div className="mt-4">
            <div className="alert alert-info">
              <p className="text-sm">
                <strong>Note:</strong> This component ensures that the exact same canonicalized payload is used
                for both signature generation and request sending, maintaining signature integrity.
              </p>
            </div>
          </div>
        </>
      )}
    </>
  )

  return (
    <Modal
      showModal={isOpen}
      onClose={onClose}
      titleHeader="Wallet Delegation"
    >
      {modalContent}
    </Modal>
  )
}

export default PrivyDelegationModal

