import { Calls } from 'ox/erc7821'
import { describe, expect, test } from 'vp/test'

describe('encode', () => {
  test('default', () => {
    expect(
      Calls.encode([
        {
          data: '0xdeadbeef',
          to: '0xcafebabecafebabecafebabecafebabecafebabe',
          value: 1n,
        },
        {
          data: '0xcafebabe',
          to: '0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef',
          value: 2n,
        },
      ]),
    ).toMatchInlineSnapshot(
      `"0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000cafebabecafebabecafebabecafebabecafebabe000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000004deadbeef00000000000000000000000000000000000000000000000000000000000000000000000000000000deadbeefdeadbeefdeadbeefdeadbeefdeadbeef000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000004cafebabe00000000000000000000000000000000000000000000000000000000"`,
    )
  })

  test('behavior: single call', () => {
    expect(
      Calls.encode([
        {
          data: '0xdeadbeef',
          to: '0xcafebabecafebabecafebabecafebabecafebabe',
          value: 1000000000000000000n,
        },
      ]),
    ).toMatchInlineSnapshot(
      `"0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000cafebabecafebabecafebabecafebabecafebabe0000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000004deadbeef00000000000000000000000000000000000000000000000000000000"`,
    )
  })

  test('behavior: no data', () => {
    expect(
      Calls.encode([
        {
          to: '0xcafebabecafebabecafebabecafebabecafebabe',
          value: 1n,
        },
        {
          to: '0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef',
          value: 2n,
        },
      ]),
    ).toMatchInlineSnapshot(
      `"0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000cafebabecafebabecafebabecafebabecafebabe000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000deadbeefdeadbeefdeadbeefdeadbeefdeadbeef000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000"`,
    )
  })

  test('behavior: no value', () => {
    expect(
      Calls.encode([
        {
          data: '0xdeadbeef',
          to: '0xcafebabecafebabecafebabecafebabecafebabe',
        },
        {
          data: '0xcafebabe',
          to: '0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef',
        },
      ]),
    ).toMatchInlineSnapshot(
      `"0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000cafebabecafebabecafebabecafebabecafebabe000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000004deadbeef00000000000000000000000000000000000000000000000000000000000000000000000000000000deadbeefdeadbeefdeadbeefdeadbeefdeadbeef000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000004cafebabe00000000000000000000000000000000000000000000000000000000"`,
    )
  })

  test('behavior: empty calls array', () => {
    expect(Calls.encode([])).toMatchInlineSnapshot(
      `"0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000"`,
    )
  })

  test('behavior: with opData', () => {
    expect(
      Calls.encode(
        [
          {
            data: '0xdeadbeef',
            to: '0xcafebabecafebabecafebabecafebabecafebabe',
            value: 1n,
          },
        ],
        { opData: '0x1234567890abcdef' },
      ),
    ).toMatchInlineSnapshot(
      `"0x0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000cafebabecafebabecafebabecafebabecafebabe000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000004deadbeef0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000081234567890abcdef000000000000000000000000000000000000000000000000"`,
    )
  })

  test('behavior: with empty opData', () => {
    expect(
      Calls.encode(
        [
          {
            data: '0xdeadbeef',
            to: '0xcafebabecafebabecafebabecafebabecafebabe',
            value: 1n,
          },
        ],
        { opData: '0x' },
      ),
    ).toMatchInlineSnapshot(
      `"0x0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000cafebabecafebabecafebabecafebabecafebabe000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000004deadbeef000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"`,
    )
  })

  test('behavior: mixed calls with and without data/value', () => {
    expect(
      Calls.encode([
        {
          to: '0xcafebabecafebabecafebabecafebabecafebabe',
          value: 100n,
        },
        {
          data: '0xdeadbeef',
          to: '0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef',
        },
        {
          data: '0xcafebabe',
          to: '0x1234567890123456789012345678901234567890',
          value: 200n,
        },
      ]),
    ).toMatchInlineSnapshot(
      `"0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000180000000000000000000000000cafebabecafebabecafebabecafebabecafebabe000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000deadbeefdeadbeefdeadbeefdeadbeefdeadbeef000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000004deadbeef00000000000000000000000000000000000000000000000000000000000000000000000000000000123456789012345678901234567890123456789000000000000000000000000000000000000000000000000000000000000000c800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000004cafebabe00000000000000000000000000000000000000000000000000000000"`,
    )
  })
})

describe('decode', () => {
  test('default', () => {
    const calls = [
      {
        data: '0xdeadbeef',
        to: '0xCafEBAbECAFEbAbEcaFEbabECAfebAbEcAFEBaBe',
        value: 1n,
      },
      {
        data: '0xcafebabe',
        to: '0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF',
        value: 2n,
      },
    ] as const

    const encoded = Calls.encode(calls)
    const result = Calls.decode(encoded)

    expect(result).toEqual({ calls })
  })

  test('behavior: single call', () => {
    const calls = [
      {
        data: '0xdeadbeef',
        to: '0xCafEBAbECAFEbAbEcaFEbabECAfebAbEcAFEBaBe',
        value: 1000000000000000000n,
      },
    ] as const

    const encoded = Calls.encode(calls)
    const result = Calls.decode(encoded)

    expect(result).toEqual({ calls })
  })

  test('behavior: empty calls', () => {
    const calls = [] as const

    const encoded = Calls.encode(calls)
    const result = Calls.decode(encoded)

    expect(result).toEqual({ calls })
  })

  test('behavior: with opData', () => {
    const calls = [
      {
        data: '0xdeadbeef',
        to: '0xCafEBAbECAFEbAbEcaFEbabECAfebAbEcAFEBaBe',
        value: 1n,
      },
    ] as const
    const opData = '0x1234567890abcdef' as const

    const encoded = Calls.encode(calls, { opData })
    const result = Calls.decode(encoded, { opData: true })

    expect(result).toEqual({ calls, opData })
  })

  test('behavior: with empty opData', () => {
    const calls = [
      {
        data: '0xdeadbeef',
        to: '0xCafEBAbECAFEbAbEcaFEbabECAfebAbEcAFEBaBe',
        value: 1n,
      },
    ] as const
    const opData = '0x' as const

    const encoded = Calls.encode(calls, { opData })
    const result = Calls.decode(encoded, { opData: true })

    expect(result).toEqual({
      calls,
      opData: undefined,
    })
  })
})
