import { VerifyContractParams } from '@/lib/functions/types'
import handleImports from '@/lib/functions/deploy-contract/handle-imports'
import { getExplorerUrl } from '@/lib/viem-utils'
import { encodeDeployData } from 'viem'
import { useNetwork, usePublicClient, useWalletClient } from 'wagmi'
import toast from 'react-hot-toast'
import { useGlobalStore } from '@/app/state/global-store'
import { track } from '@vercel/analytics'

export function useDeployWithWallet() {
  const { chain: viemChain } = useNetwork()
  const { data: walletClient } = useWalletClient()
  const publicClient = usePublicClient({
    chainId: viemChain?.id
  })
  const { setLastDeploymentData, setVerifyContractConfig } = useGlobalStore()

  async function deploy({
    contractName,
    sourceCode,
    constructorArgs
  }: {
    contractName: string
    sourceCode: string
    constructorArgs: Array<string>
  }) {
    if (!viemChain || !walletClient) {
      throw new Error('Wallet not available')
    }
    const fileName = contractName.replace(/[\/\\:*?"<>|.\s]+$/g, '_') + '.sol'

    // Prepare the sources object for the Solidity compiler
    const handleImportsResult = await handleImports(sourceCode)

    const sources = {
      [fileName]: {
        content: handleImportsResult?.sourceCode
      },
      ...handleImportsResult?.sources
    }
    const sourcesKeys = Object.keys(sources)

    // Loop over each source
    for (const sourceKey of sourcesKeys) {
      let sourceCode = sources[sourceKey].content

      // Find all import statements in the source code
      const importStatements =
        sourceCode.match(/import\s+["'][^"']+["'];/g) || []

      // Loop over each import statement
      for (const importStatement of importStatements) {
        // Extract the file name from the import statement
        const importPathMatch = importStatement.match(/["']([^"']+)["']/)

        // If no import path is found, continue to the next statement
        if (!importPathMatch) continue

        // Extract the file name from the path
        const importPath = importPathMatch[1]
        const fileName = importPath.split('/').pop() || importPath

        // Check if the file is already in the sources object
        // if (sources[fileName]) continue;

        // Replace the import statement with the new import statement
        sourceCode = sourceCode.replace(
          importStatement,
          `import "${fileName}";`
        )
      }

      // Update the source content in your sources object
      sources[sourceKey].content = sourceCode
    }

    // Compile the contract
    const standardJsonInput = JSON.stringify({
      language: 'Solidity',
      sources,
      settings: {
        evmVersion: 'paris',
        outputSelection: {
          '*': {
            '*': ['*']
          }
        },
        optimizer: {
          enabled: true,
          runs: 200
        }
      }
    })

    const compileContractResponse = await fetch('/api/compile-contract', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        standardJsonInput,
        contractName
      })
    })

    const compileResult = await compileContractResponse.json()
    const { abi, bytecode } = compileResult

    const parsedConstructorArgs = constructorArgs.map(arg => {
      if (arg.startsWith('[') && arg.endsWith(']')) {
        // Check if the string doesn't have double or single quotes after '[' and before ']'
        if (arg.match(/(?<=\[)(?=[^"'])(.*)(?<=[^"'])(?=\])/g)) {
          // Split the string by commas and remove the brackets
          const elements = arg.slice(1, -1).split(',')

          // Trim each element to remove extra spaces and return as an array
          return elements.map(item => item.trim())
        }
      }

      // Try parsing as JSON, or return the original argument
      try {
        return JSON.parse(arg)
      } catch (error) {
        return arg
      }
    })

    const deployData = encodeDeployData({
      abi: abi,
      bytecode: bytecode,
      args: parsedConstructorArgs
    })

    const [account] = await walletClient.getAddresses()

    const deployHash = await toast.promise(
      walletClient.deployContract({
        abi: abi,
        bytecode: bytecode,
        account: account,
        args: parsedConstructorArgs
      }),
      {
        loading: 'Sending deploy transaction...',
        success: 'Deploy transaction submitted!',
        error: 'Failed to deploy contract'
      }
    )

    const ipfsUploadResponse = await fetch('/api/ipfs-upload', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        sources,
        abi,
        bytecode,
        standardJsonInput
      })
    })

    const ipfsCid = await ipfsUploadResponse.json()
    const ipfsUrl = `https://nftstorage.link/ipfs/${ipfsCid}`

    const encodedConstructorArgs = deployData.slice(bytecode?.length)

    const verifyContractConfig: VerifyContractParams = {
      deployHash,
      standardJsonInput,
      encodedConstructorArgs,
      fileName,
      contractName,
      viemChain
    }

    setVerifyContractConfig(verifyContractConfig)

    const txHashExplorerUrl = getExplorerUrl(viemChain) + `/tx/${deployHash}`

    track('deployed_contract', {
      contractName,
      explorerUrl: txHashExplorerUrl
    })

    try {
      const transactionReceipt = await toast.promise(
        publicClient.waitForTransactionReceipt({
          hash: verifyContractConfig?.deployHash
        }),
        {
          loading: 'Waiting for confirmations...',
          success: 'Transaction confirmed!',
          error: 'Failed to receive enough confirmations'
        }
      )
      const address = transactionReceipt?.contractAddress || undefined

      const deploymentData = {
        address,
        transactionHash: deployHash,
        ipfsUrl,
        explorerUrl: txHashExplorerUrl,
        verificationStatus: 'pending',
        standardJsonInput,
        abi,
        sourceCode
      }

      setLastDeploymentData(deploymentData)
      return deploymentData
    } catch (error) {
      console.log(error)
      const deploymentData = {
        transactionHash: deployHash,
        explorerUrl: txHashExplorerUrl,
        ipfsUrl,
        verificationStatus: 'pending',
        standardJsonInput,
        abi,
        sourceCode
      }
      setLastDeploymentData(deploymentData)
      return deploymentData
    }
  }

  return { deploy }
}
