import { computed, ref } from 'vue';
import { useAccount } from '@wagmi/vue';
import { readContract, writeContract } from '@wagmi/core';
import { erc20Abi, maxUint256, parseEther, parseUnits, zeroAddress, type Abi } from 'viem';
import { useToast } from 'vue-toastification';
import { config as wagmiConfig } from '@/wagmi';
import { getChain, DEFAULT_CHAIN_ID } from '@/config';
import { useWallet } from '@/composables/useWallet';
import { getErrorMessage, ensureAddress } from '@harb/utils';
import { trackSwapInitiated } from '@harb/analytics';

export const WETH_ABI = [
  {
    inputs: [],
    name: 'deposit',
    outputs: [],
    stateMutability: 'payable',
    type: 'function',
  },
] as const satisfies Abi;

export const SWAP_ROUTER_ABI = [
  {
    inputs: [],
    name: 'factory',
    outputs: [{ internalType: 'address', name: '', type: 'address' }],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [
      {
        components: [
          { internalType: 'address', name: 'tokenIn', type: 'address' },
          { internalType: 'address', name: 'tokenOut', type: 'address' },
          { internalType: 'uint24', name: 'fee', type: 'uint24' },
          { internalType: 'address', name: 'recipient', type: 'address' },
          { internalType: 'uint256', name: 'amountIn', type: 'uint256' },
          { internalType: 'uint256', name: 'amountOutMinimum', type: 'uint256' },
          { internalType: 'uint160', name: 'sqrtPriceLimitX96', type: 'uint160' },
        ],
        internalType: 'struct ISwapRouter.ExactInputSingleParams',
        name: 'params',
        type: 'tuple',
      },
    ],
    name: 'exactInputSingle',
    outputs: [{ internalType: 'uint256', name: 'amountOut', type: 'uint256' }],
    stateMutability: 'payable',
    type: 'function',
  },
] as const satisfies Abi;

export const UNISWAP_FACTORY_ABI = [
  {
    inputs: [
      { internalType: 'address', name: 'tokenA', type: 'address' },
      { internalType: 'address', name: 'tokenB', type: 'address' },
      { internalType: 'uint24', name: 'fee', type: 'uint24' },
    ],
    name: 'getPool',
    outputs: [{ internalType: 'address', name: 'pool', type: 'address' }],
    stateMutability: 'view',
    type: 'function',
  },
] as const satisfies Abi;

// Full pool ABI used by both the swap flow (liquidity check) and liquidity stats (slot0).
export const UNISWAP_POOL_ABI = [
  {
    inputs: [],
    name: 'slot0',
    outputs: [
      { internalType: 'uint160', name: 'sqrtPriceX96', type: 'uint160' },
      { internalType: 'int24', name: 'tick', type: 'int24' },
      { internalType: 'uint16', name: 'observationIndex', type: 'uint16' },
      { internalType: 'uint16', name: 'observationCardinality', type: 'uint16' },
      { internalType: 'uint16', name: 'observationCardinalityNext', type: 'uint16' },
      { internalType: 'uint8', name: 'feeProtocol', type: 'uint8' },
      { internalType: 'bool', name: 'unlocked', type: 'bool' },
    ],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [],
    name: 'liquidity',
    outputs: [{ internalType: 'uint128', name: '', type: 'uint128' }],
    stateMutability: 'view',
    type: 'function',
  },
] as const satisfies Abi;

export function useSwapKrk() {
  const toast = useToast();
  const { address, chainId } = useAccount();
  const { loadBalance } = useWallet();

  const swapAmount = ref('0.1');
  const swapping = ref(false);
  const sellAmount = ref('');
  const selling = ref(false);
  const sellPhase = ref<'approving' | 'selling'>('approving');
  const krkBalance = ref<bigint>(0n);

  const resolvedChainId = computed(() => chainId.value ?? DEFAULT_CHAIN_ID);
  const chainConfig = computed(() => getChain(resolvedChainId.value));
  const cheatConfig = computed(() => chainConfig.value?.cheats ?? null);

  const canSwap = computed(() =>
    Boolean(address.value && cheatConfig.value?.weth && cheatConfig.value?.swapRouter && chainConfig.value?.harb)
  );

  async function loadKrkBalance() {
    if (!address.value || !chainConfig.value?.harb) return;
    try {
      const harb = ensureAddress(chainConfig.value.harb, 'KRAIKEN token');
      const caller = ensureAddress(address.value, 'Wallet address');
      krkBalance.value = await readContract(wagmiConfig, {
        abi: erc20Abi,
        address: harb,
        functionName: 'balanceOf',
        args: [caller],
        chainId: resolvedChainId.value,
      });
    } catch {
      // Do not overwrite a previously loaded non-zero balance on RPC error
    }
  }

  async function buyKrk() {
    if (!canSwap.value || swapping.value) return;
    try {
      swapping.value = true;

      const chain = chainConfig.value;
      const cheat = cheatConfig.value;

      if (!chain?.harb) throw new Error('No Kraiken deployment configured for this chain');
      if (!cheat) throw new Error('Swap helpers are not configured for this chain');

      const weth = ensureAddress(cheat.weth, 'WETH');
      const router = ensureAddress(cheat.swapRouter, 'Swap router');
      const harb = ensureAddress(chain.harb, 'KRAIKEN token');
      const caller = ensureAddress(address.value!, 'Wallet address');
      const currentChainId = resolvedChainId.value;

      let amount: bigint;
      try {
        amount = parseEther(swapAmount.value || '0');
      } catch {
        throw new Error('Enter a valid ETH amount');
      }
      if (amount <= 0n) throw new Error('Amount must be greater than zero');

      const factoryAddress = await readContract(wagmiConfig, {
        abi: SWAP_ROUTER_ABI,
        address: router,
        functionName: 'factory',
        chainId: currentChainId,
      });
      const factory = ensureAddress(factoryAddress, 'Uniswap factory');

      const poolAddress = await readContract(wagmiConfig, {
        abi: UNISWAP_FACTORY_ABI,
        address: factory,
        functionName: 'getPool',
        args: [weth, harb, 10_000],
        chainId: currentChainId,
      });
      if (!poolAddress || poolAddress === zeroAddress) {
        throw new Error('No KRK/WETH pool found at 1% fee; deploy and recenter first');
      }

      const poolLiquidity = await readContract(wagmiConfig, {
        abi: UNISWAP_POOL_ABI,
        address: poolAddress,
        functionName: 'liquidity',
        chainId: currentChainId,
      });
      if (poolLiquidity === 0n) {
        throw new Error('KRK/WETH pool has zero liquidity; run recenter before swapping');
      }

      const wethBalance = await readContract(wagmiConfig, {
        abi: erc20Abi,
        address: weth,
        functionName: 'balanceOf',
        args: [caller],
        chainId: currentChainId,
      });

      const wrapAmount = amount > wethBalance ? amount - wethBalance : 0n;
      if (wrapAmount > 0n) {
        await writeContract(wagmiConfig, {
          abi: WETH_ABI,
          address: weth,
          functionName: 'deposit',
          value: wrapAmount,
          chainId: currentChainId,
        });
      }

      const allowance = await readContract(wagmiConfig, {
        abi: erc20Abi,
        address: weth,
        functionName: 'allowance',
        args: [caller, router],
        chainId: currentChainId,
      });
      if (allowance < amount) {
        await writeContract(wagmiConfig, {
          abi: erc20Abi,
          address: weth,
          functionName: 'approve',
          args: [router, maxUint256],
          chainId: currentChainId,
        });
      }

      trackSwapInitiated('buy');
      await writeContract(wagmiConfig, {
        abi: SWAP_ROUTER_ABI,
        address: router,
        functionName: 'exactInputSingle',
        args: [
          {
            tokenIn: weth,
            tokenOut: harb,
            fee: 10_000,
            recipient: caller,
            amountIn: amount,
            amountOutMinimum: 0n, // no slippage protection — acceptable on no-MEV local anvil
            sqrtPriceLimitX96: 0n,
          },
        ],
        chainId: currentChainId,
      });

      await loadBalance();

      toast.success('Swap complete. $KRK has been added to your wallet.');
    } catch (error: unknown) {
      toast.error(getErrorMessage(error, 'Swap failed'));
    } finally {
      swapping.value = false;
    }
  }

  async function sellKrk() {
    if (!canSwap.value || selling.value) return;
    try {
      selling.value = true;

      const chain = chainConfig.value;
      const cheat = cheatConfig.value;

      if (!chain?.harb) throw new Error('No Kraiken deployment configured for this chain');
      if (!cheat) throw new Error('Swap helpers are not configured for this chain');

      const weth = ensureAddress(cheat.weth, 'WETH');
      const router = ensureAddress(cheat.swapRouter, 'Swap router');
      const harb = ensureAddress(chain.harb, 'KRAIKEN token');
      const caller = ensureAddress(address.value!, 'Wallet address');
      const currentChainId = resolvedChainId.value;

      let amount: bigint;
      try {
        amount = parseUnits(sellAmount.value || '0', 18);
      } catch {
        throw new Error('Enter a valid KRK amount');
      }
      if (amount <= 0n) throw new Error('Amount must be greater than zero');

      const balance = await readContract(wagmiConfig, {
        abi: erc20Abi,
        address: harb,
        functionName: 'balanceOf',
        args: [caller],
        chainId: currentChainId,
      });
      if (amount > balance) throw new Error('Insufficient KRK balance');

      const factoryAddress = await readContract(wagmiConfig, {
        abi: SWAP_ROUTER_ABI,
        address: router,
        functionName: 'factory',
        chainId: currentChainId,
      });
      const factory = ensureAddress(factoryAddress, 'Uniswap factory');

      const poolAddress = await readContract(wagmiConfig, {
        abi: UNISWAP_FACTORY_ABI,
        address: factory,
        functionName: 'getPool',
        args: [weth, harb, 10_000],
        chainId: currentChainId,
      });
      if (!poolAddress || poolAddress === zeroAddress) {
        throw new Error('No KRK/WETH pool found at 1% fee; deploy and recenter first');
      }

      const poolLiquidity = await readContract(wagmiConfig, {
        abi: UNISWAP_POOL_ABI,
        address: poolAddress,
        functionName: 'liquidity',
        chainId: currentChainId,
      });
      if (poolLiquidity === 0n) {
        throw new Error('KRK/WETH pool has zero liquidity; run recenter before swapping');
      }

      sellPhase.value = 'approving';
      const allowance = await readContract(wagmiConfig, {
        abi: erc20Abi,
        address: harb,
        functionName: 'allowance',
        args: [caller, router],
        chainId: currentChainId,
      });
      if (allowance < amount) {
        await writeContract(wagmiConfig, {
          abi: erc20Abi,
          address: harb,
          functionName: 'approve',
          args: [router, maxUint256],
          chainId: currentChainId,
        });
      }

      sellPhase.value = 'selling';
      trackSwapInitiated('sell');
      await writeContract(wagmiConfig, {
        abi: SWAP_ROUTER_ABI,
        address: router,
        functionName: 'exactInputSingle',
        args: [
          {
            tokenIn: harb,
            tokenOut: weth,
            fee: 10_000,
            recipient: caller,
            amountIn: amount,
            amountOutMinimum: 0n,
            sqrtPriceLimitX96: 0n,
          },
        ],
        chainId: currentChainId,
      });

      await loadBalance();
      await loadKrkBalance();
      sellAmount.value = '';

      toast.success('Sell complete. WETH has been added to your wallet.');
    } catch (error: unknown) {
      toast.error(getErrorMessage(error, 'Sell failed'));
    } finally {
      selling.value = false;
      sellPhase.value = 'approving';
    }
  }

  return { swapAmount, swapping, canSwap, buyKrk, sellAmount, selling, sellPhase, krkBalance, loadKrkBalance, sellKrk };
}
