// SPDX-FileCopyrightText: © 2026 LEDGER SAS
// SPDX-License-Identifier: Apache-2.0

import type { TezosCoinConfig, TezosContext } from '../config'
import { OpKind } from '@taquito/rpc'
import { mockConfig } from '../test/config'
import { craftTransaction } from '.'

/**
 * Runs against Tezos mainnet via Ledger's vault explorer (see `mockConfig`).
 * Despite the directory's other integ tests using Shadownet, this file does
 * not query a testnet — Taquito reaches the mainnet RPC for counter / reveal
 * estimation while ops themselves are forged locally and never broadcast.
 * https://api.tzkt.io/#section/Get-Started/Free-TzKT-API
 */
describe('Tezos Api', () => {
  const address = 'tz1heMGVHQnx7ALDcDKqez8fan64Eyicw4DJ'
  const context: TezosContext = {
    config: async () => mockConfig as TezosCoinConfig,
    logger: () => undefined,
  }
  it('should craft a send transaction', async () => {
    // When
    const result = await craftTransaction(
      context,
      { address },
      {
        type: 'send',
        recipient: 'tz1aWXP237BLwNHJcCD4b3DutCevhqq2T1Z9', // spell-checker: disable-line
        amount: BigInt(10),
        fee: { fees: BigInt(1).toString() },
      }
    )

    // Then
    expect(result.type).toBe('OUT')
    expect(result.contents).toEqual(
      expect.arrayContaining([
        expect.objectContaining({
          kind: OpKind.TRANSACTION,
          amount: '10',
          destination: 'tz1aWXP237BLwNHJcCD4b3DutCevhqq2T1Z9',
          source: address,
          counter: expect.any(String),
          fee: '1',
          gas_limit: '0',
          storage_limit: '0',
        }),
      ])
    )
  })

  it('should craft a delegate transaction', async () => {
    // When
    const result = await craftTransaction(
      context,
      { address },
      {
        type: 'delegate',
        recipient: 'tz1aWXP237BLwNHJcCD4b3DutCevhqq2T1Z9', // spell-checker: disable-line
        amount: BigInt(10),
        fee: { fees: BigInt(1).toString(), gasLimit: '200', storageLimit: '300' },
      }
    )

    // Then
    expect(result.type).toBe('DELEGATE')
    expect(result.contents).toEqual(
      expect.arrayContaining([
        expect.objectContaining({
          kind: OpKind.DELEGATION,
          source: address,
          counter: expect.any(String),
          delegate: 'tz1aWXP237BLwNHJcCD4b3DutCevhqq2T1Z9',
          fee: '1',
          gas_limit: '200',
          storage_limit: '300',
        }),
      ])
    )
  })

  it('should craft a undelegate transaction', async () => {
    // When
    const result = await craftTransaction(
      context,
      { address },
      {
        type: 'undelegate',
        recipient: 'tz1aWXP237BLwNHJcCD4b3DutCevhqq2T1Z9', // spell-checker: disable-line
        amount: BigInt(10),
        fee: { fees: BigInt(1).toString(), gasLimit: '200', storageLimit: '300' },
      }
    )

    // Then

    expect(result.type).toBe('UNDELEGATE')
    expect(result.contents).toEqual(
      expect.arrayContaining([
        expect.objectContaining({
          kind: OpKind.DELEGATION,
          source: address,
          counter: expect.any(String),
          fee: '1',
          gas_limit: '200',
          storage_limit: '300',
        }),
      ])
    )
  })

  it('should craft a stake transaction', async () => {
    // When
    const result = await craftTransaction(
      context,
      { address },
      {
        type: 'stake',
        recipient: '',
        amount: BigInt(1000),
        fee: { fees: BigInt(1).toString(), gasLimit: '200', storageLimit: '0' },
      }
    )

    // Then
    expect(result.type).toBe('STAKE')
    expect(result.contents).toEqual(
      expect.arrayContaining([
        expect.objectContaining({
          kind: OpKind.TRANSACTION,
          amount: '1000',
          destination: address,
          source: address,
          counter: expect.any(String),
          fee: '1',
          gas_limit: '200',
          storage_limit: '0',
          parameters: { entrypoint: 'stake', value: { prim: 'Unit' } },
        }),
      ])
    )
  })

  it('should craft a send_token (FA2) transaction', async () => {
    const contractAddress = 'KT1XnTn74bUtxHfDtBmm2bGZAQfhPbvKWR8o'

    const result = await craftTransaction(
      context,
      { address },
      {
        type: 'send_token',
        recipient: 'tz1aWXP237BLwNHJcCD4b3DutCevhqq2T1Z9', // spell-checker: disable-line
        amount: BigInt(1),
        fee: { fees: BigInt(1).toString() },
        contractAddress,
        tokenId: 0,
      }
    )

    expect(result.type).toBe('OUT')
    expect(result.contents).toEqual(
      expect.arrayContaining([
        expect.objectContaining({
          kind: OpKind.TRANSACTION,
          source: address,
          destination: contractAddress,
          amount: '0',
          counter: expect.any(String),
          fee: '1',
          gas_limit: '0',
          storage_limit: '0',
          parameters: expect.objectContaining({ entrypoint: 'transfer' }),
        }),
      ])
    )
  })

  it('should craft a send_token (FA2) transaction with reveal for unrevealed tz2 address', async () => {
    const tz2Address = 'tz2F4XnSd1wjwWsthemvZQjoPER7NVSt35k3'
    const expectedPublicKey = 'sppk7but7h93Ws1XhAPvdBcttVmoBDGHxdpaU8dPy5549f3eLJFAjag'
    const contractAddress = 'KT1XnTn74bUtxHfDtBmm2bGZAQfhPbvKWR8o'

    const result = await craftTransaction(
      context,
      { address: tz2Address },
      {
        type: 'send_token',
        recipient: 'tz1aWXP237BLwNHJcCD4b3DutCevhqq2T1Z9', // spell-checker: disable-line
        amount: BigInt(1),
        fee: { fees: '500', gasLimit: '10000', storageLimit: '0' },
        contractAddress,
        tokenId: 0,
      },
      {
        publicKey: expectedPublicKey,
        publicKeyHash: tz2Address,
      }
    )

    expect(result).toEqual({
      type: 'OUT',
      contents: [
        expect.objectContaining({
          kind: OpKind.REVEAL,
          public_key: expectedPublicKey,
          source: tz2Address,
        }),
        expect.objectContaining({
          kind: OpKind.TRANSACTION,
          source: tz2Address,
          destination: contractAddress,
          amount: '0',
          parameters: expect.objectContaining({ entrypoint: 'transfer' }),
        }),
      ],
    })
  })

  it('should craft a send transaction from tz2 address with correct compressedreveal public key', async () => {
    // Given: LAMA account from production environment
    const tz2Address = 'tz2F4XnSd1wjwWsthemvZQjoPER7NVSt35k3'
    const expectedPublicKey = 'sppk7but7h93Ws1XhAPvdBcttVmoBDGHxdpaU8dPy5549f3eLJFAjag'

    // When: craft a send transaction with the public key
    const result = await craftTransaction(
      context,
      { address: tz2Address },
      {
        type: 'send',
        recipient: 'tz2VYbGhf44HDDYP2fepNA7n7MDHWuBe6RxD',
        amount: BigInt(1000000),
        fee: { fees: '500', gasLimit: '10000', storageLimit: '0' },
      },
      {
        publicKey: expectedPublicKey,
        publicKeyHash: tz2Address,
      }
    )

    // Then: verify the reveal operation contains the correct public key
    expect(result).toEqual({
      type: 'OUT',
      contents: [
        expect.objectContaining({
          kind: OpKind.REVEAL,
          public_key: expectedPublicKey,
          source: tz2Address,
        }),
        expect.objectContaining({ kind: OpKind.TRANSACTION }),
      ],
    })
  })
})
