import { mkdtemp } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import type { Address, Hex } from 'ox'
import { Hex as ox_Hex } from 'ox'
import { KeyAuthorization } from 'ox/tempo'
import { type Address as ViemAddress, hashMessage, parseUnits } from 'viem'
import { Actions, Addresses } from 'viem/tempo'
import { describe, expect, test, vi } from 'vp/test'
import type * as z from 'zod/mini'

import { accounts, chain, getClient } from '../../test/config.js'
import { createDeviceCodeHost, submitVerify } from '../../test/deviceCode.js'
import { createJsonStorage, createServer } from '../../test/utils.js'
import type * as Rpc from '../core/zod/rpc.js'
import * as Handler from '../server/Handler.js'
import * as Provider from './Provider.js'
import * as Storage from './storage.js'

const root = accounts[0]!
const accessKey = accounts[1]!
const accessKey_2 = accounts[2]!
const expiry = Math.floor(Date.now() / 1000) + 3_600
const expiry_2 = expiry + 60

function connectRequest(
  options: {
    accessKey?: typeof accessKey | undefined
    expiry?: number | undefined
    method?: 'login' | 'register' | undefined
    showDeposit?: z.output<typeof Rpc.wallet_connect.showDeposit> | undefined
  } = {},
) {
  const { accessKey: key = accessKey, expiry: expiry_ = expiry, method, showDeposit } = options

  return {
    method: 'wallet_connect',
    params: [
      {
        capabilities: {
          authorizeAccessKey: {
            expiry: expiry_,
            keyType: key.keyType,
            publicKey: key.publicKey,
          },
          ...(method ? { method } : {}),
          ...(showDeposit !== undefined ? { showDeposit } : {}),
        },
      },
    ],
  } as const
}

async function createStoragePath() {
  return join(await mkdtemp(join(tmpdir(), 'accounts-cli-')), 'store.json')
}

async function readAccessKeys(storage: ReturnType<typeof Storage.filesystem>) {
  const value = await storage.getItem<{
    state: {
      accessKeys: {
        access: Address.Address
        address: Address.Address
        chainId: number
        expiry?: number | undefined
        handle?: { kind: string; privateKey: Hex.Hex } | undefined
        keyType: string
      }[]
    }
  }>('store')
  return value!.state.accessKeys
}

async function fund(address: ViemAddress) {
  await Actions.token.transferSync(getClient(), {
    account: root,
    feeToken: Addresses.pathUsd,
    to: address,
    token: Addresses.pathUsd,
    amount: parseUnits('10', 6),
  })
}

const transferCall = Actions.token.transfer.call({
  to: '0x0000000000000000000000000000000000000001',
  token: Addresses.pathUsd,
  amount: parseUnits('1', 6),
})

function connectCapabilities(host: ReturnType<typeof createDeviceCodeHost>, index: number) {
  const params = host.requests()[index]!.params as readonly {
    capabilities?:
      | NonNullable<Rpc.wallet_connect.Decoded['params']>[number]['capabilities']
      | undefined
  }[]
  return params[0]?.capabilities
}

describe('Provider.create', () => {
  test('default: bootstraps wallet_connect through the device-code flow', async () => {
    const host = createDeviceCodeHost()
    const server = await createServer(host.listener)
    const opened: string[] = []

    try {
      const provider = Provider.create({
        chains: [chain],
        open: async (url, prompt) => {
          opened.push(url)
          await fetch(url)
          await submitVerify(prompt)
        },
        host: `${server.url}/auth/device`,
        name: 'Accounts playground',
        storage: createJsonStorage(),
      })

      const result = await provider.request(connectRequest())
      const account = result.accounts[0]!
      const keyAuthorization = account.capabilities.keyAuthorization
        ? {
            ...account.capabilities.keyAuthorization,
            signature: {
              type: account.capabilities.keyAuthorization.signature.type,
            },
          }
        : undefined

      expect(opened).toHaveLength(1)
      expect(host.metas()).toEqual([{ name: 'Accounts playground' }])
      expect(opened[0]).toMatch(
        new RegExp(`^${server.url}/auth/device/verify\\?user_code=[A-Z]{4}-[A-Z]{4}$`),
      )
      expect({
        account: {
          address: account.address,
          capabilities: keyAuthorization ? { keyAuthorization } : {},
        },
      }).toMatchInlineSnapshot(`
        {
          "account": {
            "address": "${root.address}",
            "capabilities": {
              "keyAuthorization": {
                "address": "${accessKey.address.toLowerCase()}",
                "chainId": "${ox_Hex.fromNumber(chain.id)}",
                "expiry": "${ox_Hex.fromNumber(expiry)}",
                "keyId": "${accessKey.address.toLowerCase()}",
                "keyType": "secp256k1",
                "signature": {
                  "type": "secp256k1",
                },
              },
            },
          },
        }
      `)
    } finally {
      await server.closeAsync()
    }
  })

  test('behavior: preserves wallet_connect authorization capabilities', async () => {
    const host = createDeviceCodeHost()
    const server = await createServer(host.listener)
    let listener: Parameters<typeof createServer>[0] | undefined
    const authServer = await createServer((request, response) => {
      if (!listener) {
        const auth = Handler.auth({ origin: authServer.url })
        listener = Handler.compose([auth], { path: '/auth' }).listener
      }
      return listener(request, response)
    })

    try {
      const provider = Provider.create({
        chains: [chain],
        open: async (_url, prompt) => {
          await submitVerify(prompt)
        },
        host: `${server.url}/auth/device`,
        storage: createJsonStorage(),
      })

      const result = await provider.request({
        method: 'wallet_connect',
        params: [
          {
            capabilities: {
              auth: {
                resources: [
                  'urn:nanocodex:memory:read',
                  'urn:nanocodex:connector:github:repo:read',
                ],
                returnToken: true,
                url: `${authServer.url}/auth`,
              },
              authorizeAccessKey: {
                expiry,
                keyType: accessKey.keyType,
                publicKey: accessKey.publicKey,
                scopes: [
                  {
                    address: Addresses.pathUsd,
                    selector: 'transfer(address,uint256)',
                  },
                ],
              },
              identity: { email: true },
              method: 'login',
            },
          },
        ],
      })

      const capabilities = connectCapabilities(host, 0)
      const connected = result.accounts[0]!.capabilities
      const message = connected.personalSign?.message
      expect({
        auth: capabilities?.auth,
        authToken: connected.auth?.token ? 'present' : 'missing',
        identity: capabilities?.identity,
        personalSign: {
          keyAuthorization: connected.personalSign?.keyAuthorization ? 'present' : 'missing',
          message: message ? 'present' : 'missing',
          signature: connected.signature ? 'present' : 'missing',
          witness:
            message && connected.personalSign?.keyAuthorization
              ? KeyAuthorization.deserialize(connected.personalSign.keyAuthorization).witness ===
                hashMessage(message)
              : false,
        },
        scopes: capabilities?.authorizeAccessKey?.scopes,
      }).toMatchInlineSnapshot(`
        {
          "auth": {
            "challenge": "${authServer.url}/auth/challenge",
            "logout": "${authServer.url}/auth/logout",
            "resources": [
              "urn:nanocodex:memory:read",
              "urn:nanocodex:connector:github:repo:read",
            ],
            "returnToken": true,
            "verify": "${authServer.url}/auth",
          },
          "authToken": "present",
          "identity": {
            "email": true,
          },
          "personalSign": {
            "keyAuthorization": "present",
            "message": "present",
            "signature": "present",
            "witness": true,
          },
          "scopes": [
            {
              "address": "${Addresses.pathUsd}",
              "selector": "transfer(address,uint256)",
            },
          ],
        }
      `)
    } finally {
      await authServer.closeAsync()
      await server.closeAsync()
    }
  })

  test('behavior: forwards showDeposit through registration device-code requests', async () => {
    const host = createDeviceCodeHost()
    const server = await createServer(host.listener)

    try {
      const provider = Provider.create({
        chains: [chain],
        open: async (_url, prompt) => {
          await submitVerify(prompt)
        },
        host: `${server.url}/auth/device`,
        storage: createJsonStorage(),
      })

      await provider.request(
        connectRequest({
          method: 'register',
          showDeposit: {
            amount: '50',
            displayName: 'DoorDash',
            on: 'register',
            token: 'USDC',
          },
        }),
      )

      expect(connectCapabilities(host, 0)?.showDeposit).toMatchInlineSnapshot(`
        {
          "amount": "50",
          "displayName": "DoorDash",
          "on": "register",
          "token": "USDC",
        }
      `)
    } finally {
      await server.closeAsync()
    }
  })

  test('behavior: forwards showDeposit through login device-code requests', async () => {
    const host = createDeviceCodeHost()
    const server = await createServer(host.listener)

    try {
      const provider = Provider.create({
        chains: [chain],
        open: async (_url, prompt) => {
          await submitVerify(prompt)
        },
        host: `${server.url}/auth/device`,
        storage: createJsonStorage(),
      })

      await provider.request(connectRequest({ method: 'login', showDeposit: true }))

      expect(connectCapabilities(host, 0)?.showDeposit).toBe(true)
    } finally {
      await server.closeAsync()
    }
  })

  test('behavior: forwards showDeposit through wallet_authorizeAccessKey device-code requests', async () => {
    const host = createDeviceCodeHost()
    const server = await createServer(host.listener)

    try {
      const provider = Provider.create({
        chains: [chain],
        open: async (_url, prompt) => {
          await submitVerify(prompt)
        },
        host: `${server.url}/auth/device`,
        storage: createJsonStorage(),
      })

      await provider.request(connectRequest())
      await provider.request({
        method: 'wallet_authorizeAccessKey',
        params: [
          {
            expiry,
            keyType: accessKey.keyType,
            publicKey: accessKey.publicKey,
            showDeposit: true,
          },
        ],
      })

      const params = host.requests()[1]!.params as readonly { showDeposit?: unknown }[]
      expect(host.requests()[1]!.method).toBe('wallet_authorizeAccessKey')
      expect(params[0]?.showDeposit).toBe(true)
    } finally {
      await server.closeAsync()
    }
  })

  test('behavior: browser-open failures surface the URL and code', async () => {
    const host = createDeviceCodeHost()
    const server = await createServer(host.listener)

    try {
      const provider = Provider.create({
        chains: [chain],
        open() {
          throw new Error('browser unavailable')
        },
        host: `${server.url}/auth/device`,
        storage: createJsonStorage(),
      })

      const error = await provider.request(connectRequest()).then(
        () => undefined,
        (error: Error) => error,
      )
      expect(error?.message).toMatch(
        /^Failed to surface device code [A-Z]{4}-[A-Z]{4}\. Open .*\/auth\/device\/verify\?user_code=[A-Z]{4}-[A-Z]{4} manually\.$/,
      )
    } finally {
      await server.closeAsync()
    }
  })

  test('behavior: prints the user code when the complete verification URI is absent', async () => {
    const host = createDeviceCodeHost({ omitVerificationUriFull: true })
    const server = await createServer(host.listener)
    const write = vi.spyOn(process.stdout, 'write').mockImplementation(() => true)

    try {
      const provider = Provider.create({
        chains: [chain],
        open: async (url, prompt) => {
          expect(url).toBe(prompt.verificationUri)
          await submitVerify(prompt)
        },
        host: `${server.url}/auth/device`,
        storage: createJsonStorage(),
      })

      await provider.request(connectRequest())

      expect(write).toHaveBeenCalledWith(
        expect.stringMatching(/^Enter code [A-Z]{4}-[A-Z]{4} at http/),
      )
    } finally {
      write.mockRestore()
      await server.closeAsync()
    }
  })

  test('behavior: times out while waiting for authorization', async () => {
    const host = createDeviceCodeHost({ pollingInterval: 10 })
    const server = await createServer(host.listener)

    try {
      const provider = Provider.create({
        chains: [chain],
        open() {},
        host: `${server.url}/auth/device`,
        storage: createJsonStorage(),
        timeout: 100,
      })

      const error = await provider.request(connectRequest()).then(
        () => undefined,
        (error: Error) => error,
      )
      expect(error?.message).toMatch(
        /^Timed out waiting for device code [A-Z]{4}-[A-Z]{4}\. Continue at /,
      )
    } finally {
      await server.closeAsync()
    }
  })

  test('behavior: wallet_authorizeAccessKey requires a connected account', async () => {
    const host = createDeviceCodeHost()
    const server = await createServer(host.listener)

    try {
      const provider = Provider.create({
        chains: [chain],
        open() {
          throw new Error('Unexpected browser open.')
        },
        host: `${server.url}/auth/device`,
        storage: createJsonStorage(),
      })

      await expect(
        provider.request({
          method: 'wallet_authorizeAccessKey',
          params: [
            { expiry: expiry_2, keyType: accessKey_2.keyType, publicKey: accessKey_2.publicKey },
          ],
        }),
      ).rejects.toThrowErrorMatchingInlineSnapshot(
        `[Provider.DisconnectedError: No active account.]`,
      )
    } finally {
      await server.closeAsync()
    }
  })

  test('behavior: personal_sign requires a connected account without opening the browser', async () => {
    const host = createDeviceCodeHost()
    const server = await createServer(host.listener)

    try {
      const provider = Provider.create({
        chains: [chain],
        open() {
          throw new Error('Unexpected browser open.')
        },
        host: `${server.url}/auth/device`,
        storage: createJsonStorage(),
      })

      await expect(
        provider.request({
          method: 'personal_sign',
          params: ['0x68656c6c6f', root.address],
        }),
      ).rejects.toThrowErrorMatchingInlineSnapshot(
        `[Provider.DisconnectedError: No accounts connected.]`,
      )
      expect(host.requests()).toHaveLength(0)
    } finally {
      await server.closeAsync()
    }
  })

  test('behavior: authorizes an access key for the active account', async () => {
    const host = createDeviceCodeHost()
    const server = await createServer(host.listener)
    const opened: string[] = []

    try {
      const provider = Provider.create({
        chains: [chain],
        open: async (url, prompt) => {
          opened.push(url)
          await submitVerify(prompt)
        },
        host: `${server.url}/auth/device`,
        storage: createJsonStorage(),
      })

      await provider.request(connectRequest())

      const result = await provider.request({
        method: 'wallet_authorizeAccessKey',
        params: [
          { expiry: expiry_2, keyType: accessKey_2.keyType, publicKey: accessKey_2.publicKey },
        ],
      })
      const keyAuthorization = {
        ...result.keyAuthorization,
        signature: {
          type: result.keyAuthorization.signature.type,
        },
      }

      expect(opened).toHaveLength(2)
      expect({
        keyAuthorization,
        rootAddress: result.rootAddress,
      }).toMatchInlineSnapshot(`
        {
          "keyAuthorization": {
            "address": "${accessKey_2.address.toLowerCase()}",
            "chainId": "${ox_Hex.fromNumber(chain.id)}",
            "expiry": "${ox_Hex.fromNumber(expiry_2)}",
            "keyId": "${accessKey_2.address.toLowerCase()}",
            "keyType": "secp256k1",
            "signature": {
              "type": "secp256k1",
            },
          },
          "rootAddress": "${root.address}",
        }
      `)
    } finally {
      await server.closeAsync()
    }
  })

  test('behavior: forwards revokeAccessKey after bootstrap', async () => {
    const host = createDeviceCodeHost()
    const server = await createServer(host.listener)

    try {
      const provider = Provider.create({
        chains: [chain],
        open: async (_url, prompt) => {
          await submitVerify(prompt)
        },
        host: `${server.url}/auth/device`,
        storage: createJsonStorage(),
      })

      await provider.request(connectRequest())

      await provider.request({
        method: 'wallet_revokeAccessKey',
        params: [{ accessKeyAddress: accessKey.address, address: root.address }],
      })

      expect(host.requests().map(({ method }) => method)).toEqual([
        'wallet_connect',
        'wallet_revokeAccessKey',
      ])
    } finally {
      await server.closeAsync()
    }
  })

  test('behavior: generates, persists, and reuses a managed key during wallet_connect', async () => {
    const host = createDeviceCodeHost()
    const server = await createServer(host.listener)
    const storagePath = await createStoragePath()
    const storage = Storage.filesystem({ path: storagePath })

    try {
      const provider = Provider.create({
        chains: [chain],
        open: async (_url, prompt) => {
          await submitVerify(prompt)
        },
        host: `${server.url}/auth/device`,
        storage,
      })

      const result = await provider.request({
        method: 'wallet_connect',
        params: [{ capabilities: { authorizeAccessKey: { expiry: expiry_2 } } }],
      })
      const account = result.accounts[0]!
      await fund(account.address)

      const receipt = await provider.request({
        method: 'eth_sendTransactionSync',
        params: [{ calls: [transferCall] }],
      })
      const storage_2 = Storage.filesystem({ path: storagePath })
      const provider_2 = Provider.create({
        chains: [chain],
        open() {
          throw new Error('Unexpected browser open.')
        },
        host: `${server.url}/auth/device`,
        storage: storage_2,
      })
      const receipt_2 = await provider_2.request({
        method: 'eth_sendTransactionSync',
        params: [{ calls: [transferCall] }],
      })
      const [entry] = await readAccessKeys(storage)

      expect({
        initial: receipt.status,
        restored: receipt_2.status,
      }).toMatchInlineSnapshot(`
        {
          "initial": "0x1",
          "restored": "0x1",
        }
      `)
      expect(entry!.access).toBe('0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266')
      expect(entry!.chainId).toBe(1337)
      expect(entry!.keyType).toBe('p256')
      expect(entry!.address).toMatch(/^0x[0-9a-f]{40}$/i)
      // The CLI default is an extractable WebCrypto P-256 key (JWK handle),
      // which survives the filesystem storage round-trip above.
      expect(entry!.handle).toMatchObject({ kind: 'webcrypto-p256' })
      expect((entry!.handle as { jwk?: { d?: string } }).jwk?.d).toBeTruthy()
    } finally {
      await server.closeAsync()
    }
  })

  test('behavior: forwards signing even when a managed access key is available', async () => {
    const host = createDeviceCodeHost()
    const server = await createServer(host.listener)

    try {
      const provider = Provider.create({
        chains: [chain],
        open: async (_url, prompt) => {
          await submitVerify(prompt)
        },
        host: `${server.url}/auth/device`,
        storage: createJsonStorage(),
      })
      const connected = await provider.request({
        method: 'wallet_connect',
        params: [{ capabilities: { authorizeAccessKey: { expiry: expiry_2 } } }],
      })
      const address = connected.accounts[0]!.address

      const personal = await provider.request({
        method: 'personal_sign',
        params: ['0x68656c6c6f', address],
      })
      const typedData = await provider.request({
        method: 'eth_signTypedData_v4',
        params: [
          address,
          JSON.stringify({
            domain: {},
            message: { contents: 'hello' },
            primaryType: 'Message',
            types: {
              EIP712Domain: [],
              Message: [{ name: 'contents', type: 'string' }],
            },
          }),
        ],
      })

      expect(personal).toMatch(/^0x/)
      expect(typedData).toMatch(/^0x/)
      expect(host.requests().map((request) => request.method)).toMatchInlineSnapshot(`
        [
          "wallet_connect",
          "personal_sign",
          "eth_signTypedData_v4",
        ]
      `)
    } finally {
      await server.closeAsync()
    }
  })

  test('behavior: forwards signing with a scoped access key', async () => {
    const host = createDeviceCodeHost()
    const server = await createServer(host.listener)

    try {
      const provider = Provider.create({
        chains: [chain],
        open: async (_url, prompt) => {
          await submitVerify(prompt)
        },
        host: `${server.url}/auth/device`,
        storage: createJsonStorage(),
      })
      const connected = await provider.request({
        method: 'wallet_connect',
        params: [
          {
            capabilities: {
              authorizeAccessKey: {
                expiry: expiry_2,
                keyType: accessKey.keyType,
                publicKey: accessKey.publicKey,
                scopes: [
                  {
                    address: Addresses.pathUsd,
                    selector: 'transfer(address,uint256)',
                  },
                ],
              },
            },
          },
        ],
      })
      const address = connected.accounts[0]!.address

      const personal = await provider.request({
        method: 'personal_sign',
        params: ['0x68656c6c6f', address],
      })
      const typedData = await provider.request({
        method: 'eth_signTypedData_v4',
        params: [
          address,
          JSON.stringify({
            domain: {},
            message: { contents: 'hello' },
            primaryType: 'Message',
            types: {
              EIP712Domain: [],
              Message: [{ name: 'contents', type: 'string' }],
            },
          }),
        ],
      })

      expect({
        methods: host.requests().map((request) => request.method),
        personal: personal ? expect.any(String) : personal,
        typedData: typedData ? expect.any(String) : typedData,
      }).toMatchInlineSnapshot(`
        {
          "methods": [
            "wallet_connect",
            "personal_sign",
            "eth_signTypedData_v4",
          ],
          "personal": Any<String>,
          "typedData": Any<String>,
        }
      `)
    } finally {
      await server.closeAsync()
    }
  })

  test('behavior: generates a managed key for wallet_authorizeAccessKey without publicKey', async () => {
    const host = createDeviceCodeHost()
    const server = await createServer(host.listener)
    const storagePath = await createStoragePath()
    const storage = Storage.filesystem({ path: storagePath })

    try {
      const provider = Provider.create({
        chains: [chain],
        open: async (_url, prompt) => {
          await submitVerify(prompt)
        },
        host: `${server.url}/auth/device`,
        storage,
      })

      const connected = await provider.request({
        method: 'wallet_connect',
        params: [{ capabilities: {} }],
      })
      const result = await provider.request({
        method: 'wallet_authorizeAccessKey',
        params: [{ expiry: expiry_2 }],
      })
      await fund(result.rootAddress)

      const receipt = await provider.request({
        method: 'eth_sendTransactionSync',
        params: [{ calls: [transferCall] }],
      })
      const [entry] = await readAccessKeys(storage)

      expect(connected.accounts[0]!.address).toBe(root.address)
      expect(receipt.status).toMatchInlineSnapshot(`"0x1"`)
      expect({
        access: entry!.access,
        chainId: entry!.chainId,
      }).toMatchInlineSnapshot(`
        {
          "access": "${root.address}",
          "chainId": ${chain.id},
        }
      `)
    } finally {
      await server.closeAsync()
    }
  })

  test('behavior: wallet_updateAccessKey replaces an unpublished key authorization', async () => {
    const host = createDeviceCodeHost()
    const server = await createServer(host.listener)
    const storagePath = await createStoragePath()
    const storage = Storage.filesystem({ path: storagePath })

    try {
      const provider = Provider.create({
        chains: [chain],
        open: async (_url, prompt) => {
          await submitVerify(prompt)
        },
        host: `${server.url}/auth/device`,
        storage,
      })

      const result = await provider.request({
        method: 'wallet_connect',
        params: [
          {
            capabilities: {
              authorizeAccessKey: {
                expiry: expiry_2,
                limits: [{ limit: ox_Hex.fromNumber(1n), token: Addresses.pathUsd }],
              },
            },
          },
        ],
      })
      const account = result.accounts[0]!
      const accessKeyAddress = account.capabilities.keyAuthorization!.keyId

      const updated = parseUnits('9', 6)
      await provider.request({
        method: 'wallet_updateAccessKey',
        params: [
          {
            address: account.address,
            accessKeyAddress,
            limits: [{ limit: ox_Hex.fromNumber(updated), token: Addresses.pathUsd }],
          },
        ],
      })

      const [stored] = provider.store.accessKeys.list({
        accessKey: accessKeyAddress,
        account: account.address,
        chainId: chain.id,
      })
      expect(stored?.keyAuthorization?.limits).toEqual([
        { limit: updated, token: Addresses.pathUsd },
      ])
      expect(
        await provider.store.accessKeys.getStatus({
          accessKey: accessKeyAddress,
          account: account.address,
          chainId: chain.id,
          client: getClient(),
        }),
      ).toBe('pending')
    } finally {
      await server.closeAsync()
    }
  })

  test('behavior: wallet_updateAccessKey updates published spending limits through approval', async () => {
    const host = createDeviceCodeHost()
    const server = await createServer(host.listener)
    const storagePath = await createStoragePath()
    const storage = Storage.filesystem({ path: storagePath })

    try {
      const provider = Provider.create({
        chains: [chain],
        open: async (_url, prompt) => {
          await submitVerify(prompt)
        },
        host: `${server.url}/auth/device`,
        storage,
      })

      const result = await provider.request({
        method: 'wallet_connect',
        params: [{ capabilities: { authorizeAccessKey: { expiry: expiry_2 } } }],
      })
      const account = result.accounts[0]!
      const accessKeyAddress = account.capabilities.keyAuthorization!.keyId
      await fund(account.address)
      await provider.request({
        method: 'eth_sendTransactionSync',
        params: [{ calls: [transferCall] }],
      })

      const updated = parseUnits('9', 6)
      await provider.request({
        method: 'wallet_updateAccessKey',
        params: [
          {
            address: account.address,
            accessKeyAddress,
            limits: [{ limit: ox_Hex.fromNumber(updated), token: Addresses.pathUsd }],
          },
        ],
      })

      const { remaining } = await Actions.accessKey.getRemainingLimit(getClient(), {
        account: account.address,
        accessKey: accessKeyAddress,
        token: Addresses.pathUsd,
      })
      expect(remaining).toBe(updated)
    } finally {
      await server.closeAsync()
    }
  })

  test('behavior: regenerates a managed key when the requested key type changes', async () => {
    const host = createDeviceCodeHost()
    const server = await createServer(host.listener)
    const storagePath = await createStoragePath()
    const storage = Storage.filesystem({ path: storagePath })

    try {
      const provider = Provider.create({
        chains: [chain],
        open: async (_url, prompt) => {
          await submitVerify(prompt)
        },
        host: `${server.url}/auth/device`,
        storage,
      })

      await provider.request({
        method: 'wallet_connect',
        params: [{ capabilities: {} }],
      })
      // secp256k1 is requested explicitly; p256 is the default — a genuine
      // type change that regenerates the managed key.
      const first = await provider.request({
        method: 'wallet_authorizeAccessKey',
        params: [{ expiry, keyType: 'secp256k1' }],
      })
      const second = await provider.request({
        method: 'wallet_authorizeAccessKey',
        params: [{ expiry: expiry_2, keyType: 'p256' }],
      })
      await fund(second.rootAddress)

      const receipt = await provider.request({
        method: 'eth_sendTransactionSync',
        params: [{ calls: [transferCall] }],
      })
      const keys = await readAccessKeys(storage)

      expect(first.keyAuthorization.keyType).toBe('secp256k1')
      expect(second.keyAuthorization.keyType).toBe('p256')
      expect(first.keyAuthorization.keyId).not.toBe(second.keyAuthorization.keyId)
      expect(new Set(keys.map((key) => key.keyType))).toEqual(new Set(['secp256k1', 'p256']))
      expect(receipt.status).toBe('0x1')
    } finally {
      await server.closeAsync()
    }
  })
})
