import { encodeFunctionData, type Hex } from 'viem';
import type { ExtraTransfer } from '../orders/common.js';

export type CancelSingleChainOrderParams = {
  orderId: string;
  user: string;
  tokenIn: string;
  amountIn: bigint;
  requestedOutput: ExtraTransfer;
  extraTransfers: ExtraTransfer[];
  encodedExternalCallData: string;
  deadline: number;
  nonce: bigint;
};

export type CancelCrossChainOrderParams = {
  orderId: string;
  user: string;
  tokenIn: string;
  amountIn: bigint;
  srcChainId: number;
  deadline: number;
  minStablecoinsAmount: bigint;
  executionDetailsHash: string;
  nonce: bigint;
};

const CROSS_CHAIN_CANCEL_ORDER_ABI = [
  {
    inputs: [
      {
        components: [
          { name: 'user', type: 'address' },
          { name: 'tokenIn', type: 'address' },
          { name: 'srcChainId', type: 'uint256' },
          { name: 'deadline', type: 'uint256' },
          { name: 'amountIn', type: 'uint256' },
          { name: 'minStablecoinsAmount', type: 'uint256' },
          { name: 'executionDetailsHash', type: 'bytes32' },
          { name: 'nonce', type: 'uint256' },
        ],
        name: 'orderInfo',
        type: 'tuple',
      },
    ],
    name: 'cancelOrder',
    outputs: [],
    stateMutability: 'nonpayable',
    type: 'function',
  },
] as const;

const SINGLE_CHAIN_CANCEL_ORDER_ABI = [
  {
    inputs: [
      {
        components: [
          { name: 'amountIn', type: 'uint256' },
          { name: 'tokenIn', type: 'address' },
          { name: 'deadline', type: 'uint256' },
          { name: 'nonce', type: 'uint256' },
          { name: 'encodedExternalCallData', type: 'bytes' },
          {
            components: [
              { name: 'amount', type: 'uint256' },
              { name: 'receiver', type: 'address' },
              { name: 'token', type: 'address' },
            ],
            name: 'extraTransfers',
            type: 'tuple[]',
          },
          {
            components: [
              { name: 'amount', type: 'uint256' },
              { name: 'receiver', type: 'address' },
              { name: 'token', type: 'address' },
            ],
            name: 'requestedOutput',
            type: 'tuple',
          },
          { name: 'user', type: 'address' },
        ],
        name: 'order',
        type: 'tuple',
      },
    ],
    name: 'cancelManuallyCreatedOrder',
    outputs: [],
    stateMutability: 'nonpayable',
    type: 'function',
  },
] as const;

const PERMIT2_INVALIDATE_NONCES_ABI = [
  {
    inputs: [
      { name: 'wordPos', type: 'uint256' },
      { name: 'mask', type: 'uint256' },
    ],
    name: 'invalidateUnorderedNonces',
    outputs: [],
    stateMutability: 'nonpayable',
    type: 'function',
  },
] as const;

export async function getCancelCrossChainOrderRawData(params: CancelCrossChainOrderParams): Promise<Hex> {
  const orderInfo = {
    user: params.user as Hex,
    tokenIn: params.tokenIn as Hex,
    srcChainId: BigInt(params.srcChainId),
    deadline: BigInt(params.deadline),
    amountIn: params.amountIn,
    minStablecoinsAmount: params.minStablecoinsAmount,
    executionDetailsHash: params.executionDetailsHash as Hex,
    nonce: params.nonce,
  };

  return encodeFunctionData({
    abi: CROSS_CHAIN_CANCEL_ORDER_ABI,
    functionName: 'cancelOrder',
    args: [orderInfo],
  });
}

export async function getCancelSingleChainOrderRawData(params: CancelSingleChainOrderParams): Promise<Hex> {
  const order = {
    amountIn: params.amountIn,
    tokenIn: params.tokenIn as Hex,
    deadline: BigInt(params.deadline),
    nonce: params.nonce,
    encodedExternalCallData: params.encodedExternalCallData as Hex,
    extraTransfers: params.extraTransfers.map((transfer) => ({
      amount: transfer.amount,
      receiver: transfer.receiver as Hex,
      token: transfer.token as Hex,
    })),
    requestedOutput: {
      amount: params.requestedOutput.amount,
      receiver: params.requestedOutput.receiver as Hex,
      token: params.requestedOutput.token as Hex,
    },
    user: params.user as Hex,
  };

  return encodeFunctionData({
    abi: SINGLE_CHAIN_CANCEL_ORDER_ABI,
    functionName: 'cancelManuallyCreatedOrder',
    args: [order],
  });
}

export function getInvalidateNoncesRawData(nonce: bigint): Hex {
  const nonceWordPos = nonce >> 8n;
  const nonceBitPos = nonce - nonceWordPos * 256n;
  const mask = 1n << nonceBitPos;

  return encodeFunctionData({
    abi: PERMIT2_INVALIDATE_NONCES_ABI,
    functionName: 'invalidateUnorderedNonces',
    args: [nonceWordPos, mask],
  });
}
