import { tempo } from 'mppx/client'
import type { RpcSchema } from 'ox'
import { describe, expectTypeOf, test } from 'vp/test'

import * as Provider from './Provider.js'
import type * as Schema from './Schema.js'

type Result<method extends RpcSchema.MethodNameGeneric<Schema.Ox>> = RpcSchema.ExtractReturnType<
  Schema.Ox,
  method
>

describe('request', () => {
  test('eth_accounts', () => {
    expectTypeOf<Result<'eth_accounts'>>().toEqualTypeOf<readonly `0x${string}`[]>()
  })

  test('eth_chainId', () => {
    expectTypeOf<Result<'eth_chainId'>>().toEqualTypeOf<`0x${string}`>()
  })

  test('eth_requestAccounts', () => {
    expectTypeOf<Result<'eth_requestAccounts'>>().toEqualTypeOf<readonly `0x${string}`[]>()
  })

  test('eth_sendTransaction', () => {
    expectTypeOf<Result<'eth_sendTransaction'>>().toEqualTypeOf<`0x${string}`>()
  })

  test('wallet_connect', () => {
    expectTypeOf<Result<'wallet_connect'>>().toMatchTypeOf<{
      accounts: readonly {
        address: `0x${string}`
        capabilities: {
          accessKey?:
            | {
                address: `0x${string}`
                expiry?: number | undefined
                limits?: { token: `0x${string}`; limit: string }[] | undefined
              }
            | undefined
          identity?: { email?: string | null | undefined; idToken?: string | undefined } | undefined
          signature?: `0x${string}` | undefined
        }
      }[]
    }>()
  })

  test('wallet_disconnect', () => {
    expectTypeOf<Result<'wallet_disconnect'>>().toEqualTypeOf<undefined>()
  })

  test('wallet_swap', () => {
    expectTypeOf<Result<'wallet_swap'>>().toMatchTypeOf<{
      receipt: { transactionHash: `0x${string}` }
    }>()
  })

  test('wallet_switchEthereumChain', () => {
    expectTypeOf<Result<'wallet_switchEthereumChain'>>().toEqualTypeOf<undefined>()
  })
})

describe('create options', () => {
  test('authorizeAccessKey accepts a literal value or function', () => {
    expectTypeOf<NonNullable<Parameters<typeof Provider.create>[0]>>().toMatchTypeOf<{
      authorizeAccessKey?: Provider.create.AuthorizeAccessKey | undefined
    }>()

    Provider.create({
      authorizeAccessKey: { expiry: 123 },
    })

    Provider.create({
      authorizeAccessKey: () => undefined,
    })
  })

  test('mpp accepts session deposit options', () => {
    expectTypeOf<NonNullable<Parameters<typeof Provider.create>[0]>>().toMatchTypeOf<{
      mpp?:
        | boolean
        | {
            deposit?: string | undefined
            maxDeposit?: string | undefined
          }
        | undefined
    }>()

    Provider.create({
      mpp: {
        // @ts-expect-error accounts resolves MPP accounts internally.
        resolveAccount: () => undefined,
      },
    })
  })

  test('getMppxParameters returns mppx tempo parameters', () => {
    const provider = Provider.create({ mpp: false })
    const account = {} as ReturnType<typeof provider.getAccount>
    const parameters = provider.getMppxParameters()
    provider.getMppxParameters({ accessKey: '0x0000000000000000000000000000000000000001' })

    expectTypeOf(parameters).toMatchTypeOf<
      Pick<NonNullable<Parameters<typeof tempo>[0]>, 'getClient' | 'resolveAccount'>
    >()
    tempo({ account, ...parameters })
  })
})
