import {BskyAgent} from '@atproto/api'
import {describe, expect, it, jest} from '@jest/globals'
import {jwtDecode} from 'jwt-decode'

import {agentToSessionAccountOrThrow} from '../agent'
import {type Action, getInitialState, reducer, type State} from '../reducer'

jest.mock('jwt-decode', () => ({
  jwtDecode: jest.fn(() => ({})),
}))

const mockedJwtDecode = jest.mocked(jwtDecode)

jest.mock('../../birthdate')
jest.mock('../../../ageAssurance/data')
jest.mock('../../../ageAssurance/state', () => ({
  unsafeGetAndComputeAgeAssurance: () => ({state: {}}),
}))
jest.mock('#/lib/notifications/notifications', () => ({
  unregisterPushToken(_agents: BskyAgent[]) {
    return Promise.resolve()
  },
}))

describe('session', () => {
  it('does not throw when accessJwt is malformed', () => {
    mockedJwtDecode.mockImplementationOnce(() => {
      throw new Error('Invalid token specified: missing part #2')
    })

    const agent = new BskyAgent({service: 'https://alice.com'})
    agent.sessionManager.session = {
      active: true,
      did: 'alice-did',
      handle: 'alice.test',
      accessJwt: 'invalid-token',
      refreshJwt: 'alice-refresh-jwt-1',
    }

    expect(() => agentToSessionAccountOrThrow(agent)).not.toThrow()
    expect(agentToSessionAccountOrThrow(agent).signupQueued).toBe(false)
  })

  it('can log in and out', () => {
    let state = getInitialState([])
    expect(printState(state)).toMatchInlineSnapshot(`
      {
        "accounts": [],
        "currentAgentState": {
          "agent": {
            "service": "https://public.api.bsky.app/",
          },
          "did": undefined,
        },
        "needsPersist": false,
      }
    `)

    const agent = new BskyAgent({service: 'https://alice.com'})
    agent.sessionManager.session = {
      active: true,
      did: 'alice-did',
      handle: 'alice.test',
      accessJwt: 'alice-access-jwt-1',
      refreshJwt: 'alice-refresh-jwt-1',
    }
    state = run(state, [
      {
        type: 'switched-to-account',
        newAgent: agent,
        newAccount: agentToSessionAccountOrThrow(agent),
      },
    ])
    expect(state.currentAgentState.did).toBe('alice-did')
    expect(state.accounts.length).toBe(1)
    expect(state.accounts[0].did).toBe('alice-did')
    expect(state.accounts[0].accessJwt).toBe('alice-access-jwt-1')
    expect(state.accounts[0].refreshJwt).toBe('alice-refresh-jwt-1')
    expect(printState(state)).toMatchInlineSnapshot(`
      {
        "accounts": [
          {
            "accessJwt": "alice-access-jwt-1",
            "active": true,
            "did": "alice-did",
            "email": undefined,
            "emailAuthFactor": false,
            "emailConfirmed": false,
            "handle": "alice.test",
            "isSelfHosted": true,
            "pdsUrl": undefined,
            "refreshJwt": "alice-refresh-jwt-1",
            "service": "https://alice.com/",
            "signupQueued": false,
            "status": undefined,
          },
        ],
        "currentAgentState": {
          "agent": {
            "service": "https://alice.com/",
          },
          "did": "alice-did",
        },
        "needsPersist": true,
      }
    `)

    state = run(state, [
      {
        type: 'logged-out-every-account',
      },
    ])
    // Should keep the account but clear out the tokens.
    expect(state.currentAgentState.did).toBe(undefined)
    expect(state.accounts.length).toBe(1)
    expect(state.accounts[0].did).toBe('alice-did')
    expect(state.accounts[0].accessJwt).toBe(undefined)
    expect(state.accounts[0].refreshJwt).toBe(undefined)
    expect(printState(state)).toMatchInlineSnapshot(`
      {
        "accounts": [
          {
            "accessJwt": undefined,
            "active": true,
            "did": "alice-did",
            "email": undefined,
            "emailAuthFactor": false,
            "emailConfirmed": false,
            "handle": "alice.test",
            "isSelfHosted": true,
            "pdsUrl": undefined,
            "refreshJwt": undefined,
            "service": "https://alice.com/",
            "signupQueued": false,
            "status": undefined,
          },
        ],
        "currentAgentState": {
          "agent": {
            "service": "https://public.api.bsky.app/",
          },
          "did": undefined,
        },
        "needsPersist": true,
      }
    `)
  })

  it('switches to the latest account, stores all of them', () => {
    let state = getInitialState([])

    const agent1 = new BskyAgent({service: 'https://alice.com'})
    agent1.sessionManager.session = {
      active: true,
      did: 'alice-did',
      handle: 'alice.test',
      accessJwt: 'alice-access-jwt-1',
      refreshJwt: 'alice-refresh-jwt-1',
    }
    state = run(state, [
      {
        // Switch to Alice.
        type: 'switched-to-account',
        newAgent: agent1,
        newAccount: agentToSessionAccountOrThrow(agent1),
      },
    ])
    expect(state.accounts.length).toBe(1)
    expect(state.accounts[0].did).toBe('alice-did')
    expect(state.currentAgentState.did).toBe('alice-did')
    expect(state.currentAgentState.agent).toBe(agent1)
    expect(printState(state)).toMatchInlineSnapshot(`
      {
        "accounts": [
          {
            "accessJwt": "alice-access-jwt-1",
            "active": true,
            "did": "alice-did",
            "email": undefined,
            "emailAuthFactor": false,
            "emailConfirmed": false,
            "handle": "alice.test",
            "isSelfHosted": true,
            "pdsUrl": undefined,
            "refreshJwt": "alice-refresh-jwt-1",
            "service": "https://alice.com/",
            "signupQueued": false,
            "status": undefined,
          },
        ],
        "currentAgentState": {
          "agent": {
            "service": "https://alice.com/",
          },
          "did": "alice-did",
        },
        "needsPersist": true,
      }
    `)

    const agent2 = new BskyAgent({service: 'https://bob.com'})
    agent2.sessionManager.session = {
      active: true,
      did: 'bob-did',
      handle: 'bob.test',
      accessJwt: 'bob-access-jwt-1',
      refreshJwt: 'bob-refresh-jwt-1',
    }
    state = run(state, [
      {
        // Switch to Bob.
        type: 'switched-to-account',
        newAgent: agent2,
        newAccount: agentToSessionAccountOrThrow(agent2),
      },
    ])
    expect(state.accounts.length).toBe(2)
    // Bob should float upwards.
    expect(state.accounts[0].did).toBe('bob-did')
    expect(state.accounts[1].did).toBe('alice-did')
    expect(state.currentAgentState.did).toBe('bob-did')
    expect(state.currentAgentState.agent).toBe(agent2)
    expect(printState(state)).toMatchInlineSnapshot(`
      {
        "accounts": [
          {
            "accessJwt": "bob-access-jwt-1",
            "active": true,
            "did": "bob-did",
            "email": undefined,
            "emailAuthFactor": false,
            "emailConfirmed": false,
            "handle": "bob.test",
            "isSelfHosted": true,
            "pdsUrl": undefined,
            "refreshJwt": "bob-refresh-jwt-1",
            "service": "https://bob.com/",
            "signupQueued": false,
            "status": undefined,
          },
          {
            "accessJwt": "alice-access-jwt-1",
            "active": true,
            "did": "alice-did",
            "email": undefined,
            "emailAuthFactor": false,
            "emailConfirmed": false,
            "handle": "alice.test",
            "isSelfHosted": true,
            "pdsUrl": undefined,
            "refreshJwt": "alice-refresh-jwt-1",
            "service": "https://alice.com/",
            "signupQueued": false,
            "status": undefined,
          },
        ],
        "currentAgentState": {
          "agent": {
            "service": "https://bob.com/",
          },
          "did": "bob-did",
        },
        "needsPersist": true,
      }
    `)

    const agent3 = new BskyAgent({service: 'https://alice.com'})
    agent3.sessionManager.session = {
      active: true,
      did: 'alice-did',
      handle: 'alice-updated.test',
      accessJwt: 'alice-access-jwt-2',
      refreshJwt: 'alice-refresh-jwt-2',
    }
    state = run(state, [
      {
        // Switch back to Alice.
        type: 'switched-to-account',
        newAgent: agent3,
        newAccount: agentToSessionAccountOrThrow(agent3),
      },
    ])
    expect(state.accounts.length).toBe(2)
    // Alice should float upwards.
    expect(state.accounts[0].did).toBe('alice-did')
    expect(state.accounts[0].handle).toBe('alice-updated.test')
    expect(state.currentAgentState.did).toBe('alice-did')
    expect(state.currentAgentState.agent).toBe(agent3)
    expect(printState(state)).toMatchInlineSnapshot(`
      {
        "accounts": [
          {
            "accessJwt": "alice-access-jwt-2",
            "active": true,
            "did": "alice-did",
            "email": undefined,
            "emailAuthFactor": false,
            "emailConfirmed": false,
            "handle": "alice-updated.test",
            "isSelfHosted": true,
            "pdsUrl": undefined,
            "refreshJwt": "alice-refresh-jwt-2",
            "service": "https://alice.com/",
            "signupQueued": false,
            "status": undefined,
          },
          {
            "accessJwt": "bob-access-jwt-1",
            "active": true,
            "did": "bob-did",
            "email": undefined,
            "emailAuthFactor": false,
            "emailConfirmed": false,
            "handle": "bob.test",
            "isSelfHosted": true,
            "pdsUrl": undefined,
            "refreshJwt": "bob-refresh-jwt-1",
            "service": "https://bob.com/",
            "signupQueued": false,
            "status": undefined,
          },
        ],
        "currentAgentState": {
          "agent": {
            "service": "https://alice.com/",
          },
          "did": "alice-did",
        },
        "needsPersist": true,
      }
    `)

    const agent4 = new BskyAgent({service: 'https://jay.com'})
    agent4.sessionManager.session = {
      active: true,
      did: 'jay-did',
      handle: 'jay.test',
      accessJwt: 'jay-access-jwt-1',
      refreshJwt: 'jay-refresh-jwt-1',
    }
    state = run(state, [
      {
        // Switch to Jay.
        type: 'switched-to-account',
        newAgent: agent4,
        newAccount: agentToSessionAccountOrThrow(agent4),
      },
    ])
    expect(state.accounts.length).toBe(3)
    expect(state.accounts[0].did).toBe('jay-did')
    expect(state.currentAgentState.did).toBe('jay-did')
    expect(state.currentAgentState.agent).toBe(agent4)
    expect(printState(state)).toMatchInlineSnapshot(`
      {
        "accounts": [
          {
            "accessJwt": "jay-access-jwt-1",
            "active": true,
            "did": "jay-did",
            "email": undefined,
            "emailAuthFactor": false,
            "emailConfirmed": false,
            "handle": "jay.test",
            "isSelfHosted": true,
            "pdsUrl": undefined,
            "refreshJwt": "jay-refresh-jwt-1",
            "service": "https://jay.com/",
            "signupQueued": false,
            "status": undefined,
          },
          {
            "accessJwt": "alice-access-jwt-2",
            "active": true,
            "did": "alice-did",
            "email": undefined,
            "emailAuthFactor": false,
            "emailConfirmed": false,
            "handle": "alice-updated.test",
            "isSelfHosted": true,
            "pdsUrl": undefined,
            "refreshJwt": "alice-refresh-jwt-2",
            "service": "https://alice.com/",
            "signupQueued": false,
            "status": undefined,
          },
          {
            "accessJwt": "bob-access-jwt-1",
            "active": true,
            "did": "bob-did",
            "email": undefined,
            "emailAuthFactor": false,
            "emailConfirmed": false,
            "handle": "bob.test",
            "isSelfHosted": true,
            "pdsUrl": undefined,
            "refreshJwt": "bob-refresh-jwt-1",
            "service": "https://bob.com/",
            "signupQueued": false,
            "status": undefined,
          },
        ],
        "currentAgentState": {
          "agent": {
            "service": "https://jay.com/",
          },
          "did": "jay-did",
        },
        "needsPersist": true,
      }
    `)

    state = run(state, [
      {
        // Log everyone out.
        type: 'logged-out-every-account',
      },
    ])
    expect(state.accounts.length).toBe(3)
    expect(state.currentAgentState.did).toBe(undefined)
    // All tokens should be gone.
    expect(state.accounts[0].accessJwt).toBe(undefined)
    expect(state.accounts[0].refreshJwt).toBe(undefined)
    expect(state.accounts[1].accessJwt).toBe(undefined)
    expect(state.accounts[1].refreshJwt).toBe(undefined)
    expect(state.accounts[2].accessJwt).toBe(undefined)
    expect(state.accounts[2].refreshJwt).toBe(undefined)
    expect(printState(state)).toMatchInlineSnapshot(`
      {
        "accounts": [
          {
            "accessJwt": undefined,
            "active": true,
            "did": "jay-did",
            "email": undefined,
            "emailAuthFactor": false,
            "emailConfirmed": false,
            "handle": "jay.test",
            "isSelfHosted": true,
            "pdsUrl": undefined,
            "refreshJwt": undefined,
            "service": "https://jay.com/",
            "signupQueued": false,
            "status": undefined,
          },
          {
            "accessJwt": undefined,
            "active": true,
            "did": "alice-did",
            "email": undefined,
            "emailAuthFactor": false,
            "emailConfirmed": false,
            "handle": "alice-updated.test",
            "isSelfHosted": true,
            "pdsUrl": undefined,
            "refreshJwt": undefined,
            "service": "https://alice.com/",
            "signupQueued": false,
            "status": undefined,
          },
          {
            "accessJwt": undefined,
            "active": true,
            "did": "bob-did",
            "email": undefined,
            "emailAuthFactor": false,
            "emailConfirmed": false,
            "handle": "bob.test",
            "isSelfHosted": true,
            "pdsUrl": undefined,
            "refreshJwt": undefined,
            "service": "https://bob.com/",
            "signupQueued": false,
            "status": undefined,
          },
        ],
        "currentAgentState": {
          "agent": {
            "service": "https://public.api.bsky.app/",
          },
          "did": undefined,
        },
        "needsPersist": true,
      }
    `)
  })

  it('can log back in after logging out', () => {
    let state = getInitialState([])

    const agent1 = new BskyAgent({service: 'https://alice.com'})
    agent1.sessionManager.session = {
      active: true,
      did: 'alice-did',
      handle: 'alice.test',
      accessJwt: 'alice-access-jwt-1',
      refreshJwt: 'alice-refresh-jwt-1',
    }
    state = run(state, [
      {
        type: 'switched-to-account',
        newAgent: agent1,
        newAccount: agentToSessionAccountOrThrow(agent1),
      },
    ])
    expect(state.accounts.length).toBe(1)
    expect(state.accounts[0].accessJwt).toBe('alice-access-jwt-1')
    expect(state.accounts[0].refreshJwt).toBe('alice-refresh-jwt-1')
    expect(state.currentAgentState.did).toBe('alice-did')

    state = run(state, [
      {
        type: 'logged-out-every-account',
      },
    ])
    expect(state.accounts.length).toBe(1)
    expect(state.accounts[0].accessJwt).toBe(undefined)
    expect(state.accounts[0].refreshJwt).toBe(undefined)
    expect(state.currentAgentState.did).toBe(undefined)
    expect(printState(state)).toMatchInlineSnapshot(`
      {
        "accounts": [
          {
            "accessJwt": undefined,
            "active": true,
            "did": "alice-did",
            "email": undefined,
            "emailAuthFactor": false,
            "emailConfirmed": false,
            "handle": "alice.test",
            "isSelfHosted": true,
            "pdsUrl": undefined,
            "refreshJwt": undefined,
            "service": "https://alice.com/",
            "signupQueued": false,
            "status": undefined,
          },
        ],
        "currentAgentState": {
          "agent": {
            "service": "https://public.api.bsky.app/",
          },
          "did": undefined,
        },
        "needsPersist": true,
      }
    `)

    const agent2 = new BskyAgent({service: 'https://alice.com'})
    agent2.sessionManager.session = {
      active: true,
      did: 'alice-did',
      handle: 'alice.test',
      accessJwt: 'alice-access-jwt-2',
      refreshJwt: 'alice-refresh-jwt-2',
    }
    state = run(state, [
      {
        type: 'switched-to-account',
        newAgent: agent2,
        newAccount: agentToSessionAccountOrThrow(agent2),
      },
    ])
    expect(state.accounts.length).toBe(1)
    expect(state.accounts[0].accessJwt).toBe('alice-access-jwt-2')
    expect(state.accounts[0].refreshJwt).toBe('alice-refresh-jwt-2')
    expect(state.currentAgentState.did).toBe('alice-did')
    expect(printState(state)).toMatchInlineSnapshot(`
      {
        "accounts": [
          {
            "accessJwt": "alice-access-jwt-2",
            "active": true,
            "did": "alice-did",
            "email": undefined,
            "emailAuthFactor": false,
            "emailConfirmed": false,
            "handle": "alice.test",
            "isSelfHosted": true,
            "pdsUrl": undefined,
            "refreshJwt": "alice-refresh-jwt-2",
            "service": "https://alice.com/",
            "signupQueued": false,
            "status": undefined,
          },
        ],
        "currentAgentState": {
          "agent": {
            "service": "https://alice.com/",
          },
          "did": "alice-did",
        },
        "needsPersist": true,
      }
    `)
  })

  it('can remove active account', () => {
    let state = getInitialState([])

    const agent1 = new BskyAgent({service: 'https://alice.com'})
    agent1.sessionManager.session = {
      active: true,
      did: 'alice-did',
      handle: 'alice.test',
      accessJwt: 'alice-access-jwt-1',
      refreshJwt: 'alice-refresh-jwt-1',
    }
    state = run(state, [
      {
        type: 'switched-to-account',
        newAgent: agent1,
        newAccount: agentToSessionAccountOrThrow(agent1),
      },
    ])
    expect(state.accounts.length).toBe(1)
    expect(state.accounts[0].accessJwt).toBe('alice-access-jwt-1')
    expect(state.accounts[0].refreshJwt).toBe('alice-refresh-jwt-1')
    expect(state.currentAgentState.did).toBe('alice-did')

    state = run(state, [
      {
        type: 'removed-account',
        accountDid: 'alice-did',
      },
    ])
    expect(state.accounts.length).toBe(0)
    expect(state.currentAgentState.did).toBe(undefined)
    expect(printState(state)).toMatchInlineSnapshot(`
      {
        "accounts": [],
        "currentAgentState": {
          "agent": {
            "service": "https://public.api.bsky.app/",
          },
          "did": undefined,
        },
        "needsPersist": true,
      }
    `)
  })

  it('can remove inactive account', () => {
    let state = getInitialState([])

    const agent1 = new BskyAgent({service: 'https://alice.com'})
    agent1.sessionManager.session = {
      active: true,
      did: 'alice-did',
      handle: 'alice.test',
      accessJwt: 'alice-access-jwt-1',
      refreshJwt: 'alice-refresh-jwt-1',
    }
    const agent2 = new BskyAgent({service: 'https://bob.com'})
    agent2.sessionManager.session = {
      active: true,
      did: 'bob-did',
      handle: 'bob.test',
      accessJwt: 'bob-access-jwt-1',
      refreshJwt: 'bob-refresh-jwt-1',
    }
    state = run(state, [
      {
        type: 'switched-to-account',
        newAgent: agent1,
        newAccount: agentToSessionAccountOrThrow(agent1),
      },
      {
        type: 'switched-to-account',
        newAgent: agent2,
        newAccount: agentToSessionAccountOrThrow(agent2),
      },
    ])
    expect(state.accounts.length).toBe(2)
    expect(state.currentAgentState.did).toBe('bob-did')

    state = run(state, [
      {
        type: 'removed-account',
        accountDid: 'alice-did',
      },
    ])
    expect(state.accounts.length).toBe(1)
    expect(state.currentAgentState.did).toBe('bob-did')
    expect(printState(state)).toMatchInlineSnapshot(`
      {
        "accounts": [
          {
            "accessJwt": "bob-access-jwt-1",
            "active": true,
            "did": "bob-did",
            "email": undefined,
            "emailAuthFactor": false,
            "emailConfirmed": false,
            "handle": "bob.test",
            "isSelfHosted": true,
            "pdsUrl": undefined,
            "refreshJwt": "bob-refresh-jwt-1",
            "service": "https://bob.com/",
            "signupQueued": false,
            "status": undefined,
          },
        ],
        "currentAgentState": {
          "agent": {
            "service": "https://bob.com/",
          },
          "did": "bob-did",
        },
        "needsPersist": true,
      }
    `)

    state = run(state, [
      {
        type: 'removed-account',
        accountDid: 'bob-did',
      },
    ])
    expect(state.accounts.length).toBe(0)
    expect(state.currentAgentState.did).toBe(undefined)
  })

  it('can log out of the current account', () => {
    let state = getInitialState([])

    const agent1 = new BskyAgent({service: 'https://alice.com'})
    agent1.sessionManager.session = {
      active: true,
      did: 'alice-did',
      handle: 'alice.test',
      accessJwt: 'alice-access-jwt-1',
      refreshJwt: 'alice-refresh-jwt-1',
    }
    state = run(state, [
      {
        type: 'switched-to-account',
        newAgent: agent1,
        newAccount: agentToSessionAccountOrThrow(agent1),
      },
    ])
    expect(state.accounts.length).toBe(1)
    expect(state.accounts[0].accessJwt).toBe('alice-access-jwt-1')
    expect(state.accounts[0].refreshJwt).toBe('alice-refresh-jwt-1')
    expect(state.currentAgentState.did).toBe('alice-did')

    const agent2 = new BskyAgent({service: 'https://bob.com'})
    agent2.sessionManager.session = {
      active: true,
      did: 'bob-did',
      handle: 'bob.test',
      accessJwt: 'bob-access-jwt-1',
      refreshJwt: 'bob-refresh-jwt-1',
    }
    state = run(state, [
      {
        type: 'switched-to-account',
        newAgent: agent2,
        newAccount: agentToSessionAccountOrThrow(agent2),
      },
    ])
    expect(state.accounts.length).toBe(2)
    expect(state.accounts[0].accessJwt).toBe('bob-access-jwt-1')
    expect(state.accounts[0].refreshJwt).toBe('bob-refresh-jwt-1')
    expect(state.currentAgentState.did).toBe('bob-did')

    state = run(state, [
      {
        type: 'logged-out-current-account',
      },
    ])
    expect(state.accounts.length).toBe(2)
    expect(state.accounts[0].accessJwt).toBe(undefined)
    expect(state.accounts[0].refreshJwt).toBe(undefined)
    expect(state.accounts[1].accessJwt).toBe('alice-access-jwt-1')
    expect(state.accounts[1].refreshJwt).toBe('alice-refresh-jwt-1')
    expect(state.currentAgentState.did).toBe(undefined)
    expect(printState(state)).toMatchInlineSnapshot(`
      {
        "accounts": [
          {
            "accessJwt": undefined,
            "active": true,
            "did": "bob-did",
            "email": undefined,
            "emailAuthFactor": false,
            "emailConfirmed": false,
            "handle": "bob.test",
            "isSelfHosted": true,
            "pdsUrl": undefined,
            "refreshJwt": undefined,
            "service": "https://bob.com/",
            "signupQueued": false,
            "status": undefined,
          },
          {
            "accessJwt": "alice-access-jwt-1",
            "active": true,
            "did": "alice-did",
            "email": undefined,
            "emailAuthFactor": false,
            "emailConfirmed": false,
            "handle": "alice.test",
            "isSelfHosted": true,
            "pdsUrl": undefined,
            "refreshJwt": "alice-refresh-jwt-1",
            "service": "https://alice.com/",
            "signupQueued": false,
            "status": undefined,
          },
        ],
        "currentAgentState": {
          "agent": {
            "service": "https://public.api.bsky.app/",
          },
          "did": undefined,
        },
        "needsPersist": true,
      }
    `)
  })

  it('updates stored account with refreshed tokens', () => {
    let state = getInitialState([])

    const agent1 = new BskyAgent({service: 'https://alice.com'})
    agent1.sessionManager.session = {
      active: true,
      did: 'alice-did',
      handle: 'alice.test',
      accessJwt: 'alice-access-jwt-1',
      refreshJwt: 'alice-refresh-jwt-1',
    }
    state = run(state, [
      {
        type: 'switched-to-account',
        newAgent: agent1,
        newAccount: agentToSessionAccountOrThrow(agent1),
      },
    ])
    expect(state.accounts.length).toBe(1)
    expect(state.currentAgentState.did).toBe('alice-did')

    agent1.sessionManager.session = {
      active: true,
      did: 'alice-did',
      handle: 'alice-updated.test',
      accessJwt: 'alice-access-jwt-2',
      refreshJwt: 'alice-refresh-jwt-2',
      email: 'alice@foo.bar',
      emailAuthFactor: false,
      emailConfirmed: false,
    }
    state = run(state, [
      {
        type: 'received-agent-event',
        accountDid: 'alice-did',
        agent: agent1,
        refreshedAccount: agentToSessionAccountOrThrow(agent1),
        sessionEvent: 'update',
      },
    ])
    expect(state.accounts.length).toBe(1)
    expect(state.accounts[0].email).toBe('alice@foo.bar')
    expect(state.accounts[0].handle).toBe('alice-updated.test')
    expect(state.accounts[0].accessJwt).toBe('alice-access-jwt-2')
    expect(state.accounts[0].refreshJwt).toBe('alice-refresh-jwt-2')
    expect(state.currentAgentState.did).toBe('alice-did')
    expect(printState(state)).toMatchInlineSnapshot(`
      {
        "accounts": [
          {
            "accessJwt": "alice-access-jwt-2",
            "active": true,
            "did": "alice-did",
            "email": "alice@foo.bar",
            "emailAuthFactor": false,
            "emailConfirmed": false,
            "handle": "alice-updated.test",
            "isSelfHosted": true,
            "pdsUrl": undefined,
            "refreshJwt": "alice-refresh-jwt-2",
            "service": "https://alice.com/",
            "signupQueued": false,
            "status": undefined,
          },
        ],
        "currentAgentState": {
          "agent": {
            "service": "https://alice.com/",
          },
          "did": "alice-did",
        },
        "needsPersist": true,
      }
    `)

    agent1.sessionManager.session = {
      active: true,
      did: 'alice-did',
      handle: 'alice-updated.test',
      accessJwt: 'alice-access-jwt-3',
      refreshJwt: 'alice-refresh-jwt-3',
      email: 'alice@foo.baz',
      emailAuthFactor: true,
      emailConfirmed: true,
    }
    state = run(state, [
      {
        type: 'received-agent-event',
        accountDid: 'alice-did',
        agent: agent1,
        refreshedAccount: agentToSessionAccountOrThrow(agent1),
        sessionEvent: 'update',
      },
    ])
    expect(state.accounts.length).toBe(1)
    expect(state.accounts[0].email).toBe('alice@foo.baz')
    expect(state.accounts[0].handle).toBe('alice-updated.test')
    expect(state.accounts[0].accessJwt).toBe('alice-access-jwt-3')
    expect(state.accounts[0].refreshJwt).toBe('alice-refresh-jwt-3')
    expect(state.currentAgentState.did).toBe('alice-did')
    expect(printState(state)).toMatchInlineSnapshot(`
      {
        "accounts": [
          {
            "accessJwt": "alice-access-jwt-3",
            "active": true,
            "did": "alice-did",
            "email": "alice@foo.baz",
            "emailAuthFactor": true,
            "emailConfirmed": true,
            "handle": "alice-updated.test",
            "isSelfHosted": true,
            "pdsUrl": undefined,
            "refreshJwt": "alice-refresh-jwt-3",
            "service": "https://alice.com/",
            "signupQueued": false,
            "status": undefined,
          },
        ],
        "currentAgentState": {
          "agent": {
            "service": "https://alice.com/",
          },
          "did": "alice-did",
        },
        "needsPersist": true,
      }
    `)

    agent1.sessionManager.session = {
      active: true,
      did: 'alice-did',
      handle: 'alice-updated.test',
      accessJwt: 'alice-access-jwt-4',
      refreshJwt: 'alice-refresh-jwt-4',
      email: 'alice@foo.baz',
      emailAuthFactor: false,
      emailConfirmed: false,
    }
    state = run(state, [
      {
        type: 'received-agent-event',
        accountDid: 'alice-did',
        agent: agent1,
        refreshedAccount: agentToSessionAccountOrThrow(agent1),
        sessionEvent: 'update',
      },
    ])
    expect(state.accounts.length).toBe(1)
    expect(state.accounts[0].email).toBe('alice@foo.baz')
    expect(state.accounts[0].handle).toBe('alice-updated.test')
    expect(state.accounts[0].accessJwt).toBe('alice-access-jwt-4')
    expect(state.accounts[0].refreshJwt).toBe('alice-refresh-jwt-4')
    expect(state.currentAgentState.did).toBe('alice-did')
    expect(printState(state)).toMatchInlineSnapshot(`
      {
        "accounts": [
          {
            "accessJwt": "alice-access-jwt-4",
            "active": true,
            "did": "alice-did",
            "email": "alice@foo.baz",
            "emailAuthFactor": false,
            "emailConfirmed": false,
            "handle": "alice-updated.test",
            "isSelfHosted": true,
            "pdsUrl": undefined,
            "refreshJwt": "alice-refresh-jwt-4",
            "service": "https://alice.com/",
            "signupQueued": false,
            "status": undefined,
          },
        ],
        "currentAgentState": {
          "agent": {
            "service": "https://alice.com/",
          },
          "did": "alice-did",
        },
        "needsPersist": true,
      }
    `)
  })

  it('bails out of update on identical objects', () => {
    let state = getInitialState([])

    const agent1 = new BskyAgent({service: 'https://alice.com'})
    agent1.sessionManager.session = {
      active: true,
      did: 'alice-did',
      handle: 'alice.test',
      accessJwt: 'alice-access-jwt-1',
      refreshJwt: 'alice-refresh-jwt-1',
    }
    state = run(state, [
      {
        type: 'switched-to-account',
        newAgent: agent1,
        newAccount: agentToSessionAccountOrThrow(agent1),
      },
    ])
    expect(state.accounts.length).toBe(1)
    expect(state.currentAgentState.did).toBe('alice-did')

    agent1.sessionManager.session = {
      active: true,
      did: 'alice-did',
      handle: 'alice-updated.test',
      accessJwt: 'alice-access-jwt-2',
      refreshJwt: 'alice-refresh-jwt-2',
    }
    state = run(state, [
      {
        type: 'received-agent-event',
        accountDid: 'alice-did',
        agent: agent1,
        refreshedAccount: agentToSessionAccountOrThrow(agent1),
        sessionEvent: 'update',
      },
    ])
    expect(state.accounts.length).toBe(1)
    expect(state.accounts[0].accessJwt).toBe('alice-access-jwt-2')

    const lastState = state
    state = run(state, [
      {
        type: 'received-agent-event',
        accountDid: 'alice-did',
        agent: agent1,
        refreshedAccount: agentToSessionAccountOrThrow(agent1),
        sessionEvent: 'update',
      },
    ])
    expect(lastState === state).toBe(true)

    agent1.sessionManager.session = {
      active: true,
      did: 'alice-did',
      handle: 'alice-updated.test',
      accessJwt: 'alice-access-jwt-3',
      refreshJwt: 'alice-refresh-jwt-3',
    }
    state = run(state, [
      {
        type: 'received-agent-event',
        accountDid: 'alice-did',
        agent: agent1,
        refreshedAccount: agentToSessionAccountOrThrow(agent1),
        sessionEvent: 'update',
      },
    ])
    expect(state.accounts.length).toBe(1)
    expect(state.accounts[0].accessJwt).toBe('alice-access-jwt-3')
  })

  it('accepts updates from a stale agent', () => {
    let state = getInitialState([])

    const agent1 = new BskyAgent({service: 'https://alice.com'})
    agent1.sessionManager.session = {
      active: true,
      did: 'alice-did',
      handle: 'alice.test',
      accessJwt: 'alice-access-jwt-1',
      refreshJwt: 'alice-refresh-jwt-1',
    }

    const agent2 = new BskyAgent({service: 'https://bob.com'})
    agent2.sessionManager.session = {
      active: true,
      did: 'bob-did',
      handle: 'bob.test',
      accessJwt: 'bob-access-jwt-1',
      refreshJwt: 'bob-refresh-jwt-1',
    }

    state = run(state, [
      {
        // Switch to Alice.
        type: 'switched-to-account',
        newAgent: agent1,
        newAccount: agentToSessionAccountOrThrow(agent1),
      },
      {
        // Switch to Bob.
        type: 'switched-to-account',
        newAgent: agent2,
        newAccount: agentToSessionAccountOrThrow(agent2),
      },
    ])
    expect(state.accounts.length).toBe(2)
    expect(state.currentAgentState.did).toBe('bob-did')

    agent1.sessionManager.session = {
      active: true,
      did: 'alice-did',
      handle: 'alice-updated.test',
      accessJwt: 'alice-access-jwt-2',
      refreshJwt: 'alice-refresh-jwt-2',
      email: 'alice@foo.bar',
      emailAuthFactor: false,
      emailConfirmed: false,
    }
    state = run(state, [
      {
        type: 'received-agent-event',
        accountDid: 'alice-did',
        agent: agent1,
        refreshedAccount: agentToSessionAccountOrThrow(agent1),
        sessionEvent: 'update',
      },
    ])
    expect(state.accounts.length).toBe(2)
    expect(state.accounts[1].did).toBe('alice-did')
    // Should update Alice's tokens because otherwise they'll be stale.
    expect(state.accounts[1].handle).toBe('alice-updated.test')
    expect(state.accounts[1].accessJwt).toBe('alice-access-jwt-2')
    expect(state.accounts[1].refreshJwt).toBe('alice-refresh-jwt-2')
    expect(printState(state)).toMatchInlineSnapshot(`
      {
        "accounts": [
          {
            "accessJwt": "bob-access-jwt-1",
            "active": true,
            "did": "bob-did",
            "email": undefined,
            "emailAuthFactor": false,
            "emailConfirmed": false,
            "handle": "bob.test",
            "isSelfHosted": true,
            "pdsUrl": undefined,
            "refreshJwt": "bob-refresh-jwt-1",
            "service": "https://bob.com/",
            "signupQueued": false,
            "status": undefined,
          },
          {
            "accessJwt": "alice-access-jwt-2",
            "active": true,
            "did": "alice-did",
            "email": "alice@foo.bar",
            "emailAuthFactor": false,
            "emailConfirmed": false,
            "handle": "alice-updated.test",
            "isSelfHosted": true,
            "pdsUrl": undefined,
            "refreshJwt": "alice-refresh-jwt-2",
            "service": "https://alice.com/",
            "signupQueued": false,
            "status": undefined,
          },
        ],
        "currentAgentState": {
          "agent": {
            "service": "https://bob.com/",
          },
          "did": "bob-did",
        },
        "needsPersist": true,
      }
    `)

    agent2.sessionManager.session = {
      active: true,
      did: 'bob-did',
      handle: 'bob-updated.test',
      accessJwt: 'bob-access-jwt-2',
      refreshJwt: 'bob-refresh-jwt-2',
    }
    state = run(state, [
      {
        // Update Bob.
        type: 'received-agent-event',
        accountDid: 'bob-did',
        agent: agent2,
        refreshedAccount: agentToSessionAccountOrThrow(agent2),
        sessionEvent: 'update',
      },
    ])
    expect(state.accounts.length).toBe(2)
    expect(state.accounts[0].did).toBe('bob-did')
    // Should update Bob's tokens because otherwise they'll be stale.
    expect(state.accounts[0].handle).toBe('bob-updated.test')
    expect(state.accounts[0].accessJwt).toBe('bob-access-jwt-2')
    expect(state.accounts[0].refreshJwt).toBe('bob-refresh-jwt-2')
    expect(printState(state)).toMatchInlineSnapshot(`
      {
        "accounts": [
          {
            "accessJwt": "bob-access-jwt-2",
            "active": true,
            "did": "bob-did",
            "email": undefined,
            "emailAuthFactor": false,
            "emailConfirmed": false,
            "handle": "bob-updated.test",
            "isSelfHosted": true,
            "pdsUrl": undefined,
            "refreshJwt": "bob-refresh-jwt-2",
            "service": "https://bob.com/",
            "signupQueued": false,
            "status": undefined,
          },
          {
            "accessJwt": "alice-access-jwt-2",
            "active": true,
            "did": "alice-did",
            "email": "alice@foo.bar",
            "emailAuthFactor": false,
            "emailConfirmed": false,
            "handle": "alice-updated.test",
            "isSelfHosted": true,
            "pdsUrl": undefined,
            "refreshJwt": "alice-refresh-jwt-2",
            "service": "https://alice.com/",
            "signupQueued": false,
            "status": undefined,
          },
        ],
        "currentAgentState": {
          "agent": {
            "service": "https://bob.com/",
          },
          "did": "bob-did",
        },
        "needsPersist": true,
      }
    `)

    // Ignore other events for inactive agent.
    const lastState = state
    agent1.sessionManager.session = undefined
    state = run(state, [
      {
        type: 'received-agent-event',
        accountDid: 'alice-did',
        agent: agent1,
        refreshedAccount: undefined,
        sessionEvent: 'network-error',
      },
    ])
    expect(lastState === state).toBe(true)
    state = run(state, [
      {
        type: 'received-agent-event',
        accountDid: 'alice-did',
        agent: agent1,
        refreshedAccount: undefined,
        sessionEvent: 'expired',
      },
    ])
    expect(lastState === state).toBe(true)
  })

  it('ignores updates from a removed agent', () => {
    let state = getInitialState([])

    const agent1 = new BskyAgent({service: 'https://alice.com'})
    agent1.sessionManager.session = {
      active: true,
      did: 'alice-did',
      handle: 'alice.test',
      accessJwt: 'alice-access-jwt-1',
      refreshJwt: 'alice-refresh-jwt-1',
    }

    const agent2 = new BskyAgent({service: 'https://bob.com'})
    agent2.sessionManager.session = {
      active: true,
      did: 'bob-did',
      handle: 'bob.test',
      accessJwt: 'bob-access-jwt-1',
      refreshJwt: 'bob-refresh-jwt-1',
    }

    state = run(state, [
      {
        type: 'switched-to-account',
        newAgent: agent1,
        newAccount: agentToSessionAccountOrThrow(agent1),
      },
      {
        type: 'switched-to-account',
        newAgent: agent2,
        newAccount: agentToSessionAccountOrThrow(agent2),
      },
      {
        type: 'removed-account',
        accountDid: 'alice-did',
      },
    ])
    expect(state.accounts.length).toBe(1)
    expect(state.currentAgentState.did).toBe('bob-did')

    agent1.sessionManager.session = {
      active: true,
      did: 'alice-did',
      handle: 'alice.test',
      accessJwt: 'alice-access-jwt-2',
      refreshJwt: 'alice-refresh-jwt-2',
    }
    state = run(state, [
      {
        type: 'received-agent-event',
        accountDid: 'alice-did',
        agent: agent1,
        refreshedAccount: agentToSessionAccountOrThrow(agent1),
        sessionEvent: 'update',
      },
    ])
    expect(state.accounts.length).toBe(1)
    expect(state.accounts[0].did).toBe('bob-did')
    expect(state.accounts[0].accessJwt).toBe('bob-access-jwt-1')
    expect(state.currentAgentState.did).toBe('bob-did')
  })

  it('ignores network errors', () => {
    let state = getInitialState([])

    const agent1 = new BskyAgent({service: 'https://alice.com'})
    agent1.sessionManager.session = {
      active: true,
      did: 'alice-did',
      handle: 'alice.test',
      accessJwt: 'alice-access-jwt-1',
      refreshJwt: 'alice-refresh-jwt-1',
    }
    state = run(state, [
      {
        // Switch to Alice.
        type: 'switched-to-account',
        newAgent: agent1,
        newAccount: agentToSessionAccountOrThrow(agent1),
      },
    ])
    expect(state.accounts.length).toBe(1)
    expect(state.currentAgentState.did).toBe('alice-did')

    agent1.sessionManager.session = undefined
    state = run(state, [
      {
        type: 'received-agent-event',
        accountDid: 'alice-did',
        agent: agent1,
        refreshedAccount: undefined,
        sessionEvent: 'network-error',
      },
    ])
    expect(state.accounts.length).toBe(1)
    expect(state.accounts[0].accessJwt).toBe('alice-access-jwt-1')
    expect(state.accounts[0].refreshJwt).toBe('alice-refresh-jwt-1')
    expect(state.currentAgentState.did).toBe('alice-did')
    expect(printState(state)).toMatchInlineSnapshot(`
      {
        "accounts": [
          {
            "accessJwt": "alice-access-jwt-1",
            "active": true,
            "did": "alice-did",
            "email": undefined,
            "emailAuthFactor": false,
            "emailConfirmed": false,
            "handle": "alice.test",
            "isSelfHosted": true,
            "pdsUrl": undefined,
            "refreshJwt": "alice-refresh-jwt-1",
            "service": "https://alice.com/",
            "signupQueued": false,
            "status": undefined,
          },
        ],
        "currentAgentState": {
          "agent": {
            "service": "https://alice.com/",
          },
          "did": "alice-did",
        },
        "needsPersist": true,
      }
    `)
  })

  it('resets tokens on expired event', () => {
    let state = getInitialState([])

    const agent1 = new BskyAgent({service: 'https://alice.com'})
    agent1.sessionManager.session = {
      active: true,
      did: 'alice-did',
      handle: 'alice.test',
      accessJwt: 'alice-access-jwt-1',
      refreshJwt: 'alice-refresh-jwt-1',
    }
    state = run(state, [
      {
        type: 'switched-to-account',
        newAgent: agent1,
        newAccount: agentToSessionAccountOrThrow(agent1),
      },
    ])
    expect(state.accounts.length).toBe(1)
    expect(state.accounts[0].accessJwt).toBe('alice-access-jwt-1')
    expect(state.currentAgentState.did).toBe('alice-did')

    agent1.sessionManager.session = undefined
    state = run(state, [
      {
        type: 'received-agent-event',
        accountDid: 'alice-did',
        agent: agent1,
        refreshedAccount: undefined,
        sessionEvent: 'expired',
      },
    ])
    expect(state.accounts.length).toBe(1)
    expect(state.accounts[0].accessJwt).toBe(undefined)
    expect(state.accounts[0].refreshJwt).toBe(undefined)
    expect(state.currentAgentState.did).toBe(undefined)
    expect(printState(state)).toMatchInlineSnapshot(`
      {
        "accounts": [
          {
            "accessJwt": undefined,
            "active": true,
            "did": "alice-did",
            "email": undefined,
            "emailAuthFactor": false,
            "emailConfirmed": false,
            "handle": "alice.test",
            "isSelfHosted": true,
            "pdsUrl": undefined,
            "refreshJwt": undefined,
            "service": "https://alice.com/",
            "signupQueued": false,
            "status": undefined,
          },
        ],
        "currentAgentState": {
          "agent": {
            "service": "https://public.api.bsky.app/",
          },
          "did": undefined,
        },
        "needsPersist": true,
      }
    `)
  })

  it('resets tokens on created-failed event', () => {
    let state = getInitialState([])

    const agent1 = new BskyAgent({service: 'https://alice.com'})
    agent1.sessionManager.session = {
      active: true,
      did: 'alice-did',
      handle: 'alice.test',
      accessJwt: 'alice-access-jwt-1',
      refreshJwt: 'alice-refresh-jwt-1',
    }
    state = run(state, [
      {
        type: 'switched-to-account',
        newAgent: agent1,
        newAccount: agentToSessionAccountOrThrow(agent1),
      },
    ])
    expect(state.accounts.length).toBe(1)
    expect(state.accounts[0].accessJwt).toBe('alice-access-jwt-1')
    expect(state.currentAgentState.did).toBe('alice-did')

    agent1.sessionManager.session = undefined
    state = run(state, [
      {
        type: 'received-agent-event',
        accountDid: 'alice-did',
        agent: agent1,
        refreshedAccount: undefined,
        sessionEvent: 'create-failed',
      },
    ])
    expect(state.accounts.length).toBe(1)
    expect(state.accounts[0].accessJwt).toBe(undefined)
    expect(state.accounts[0].refreshJwt).toBe(undefined)
    expect(state.currentAgentState.did).toBe(undefined)
    expect(printState(state)).toMatchInlineSnapshot(`
      {
        "accounts": [
          {
            "accessJwt": undefined,
            "active": true,
            "did": "alice-did",
            "email": undefined,
            "emailAuthFactor": false,
            "emailConfirmed": false,
            "handle": "alice.test",
            "isSelfHosted": true,
            "pdsUrl": undefined,
            "refreshJwt": undefined,
            "service": "https://alice.com/",
            "signupQueued": false,
            "status": undefined,
          },
        ],
        "currentAgentState": {
          "agent": {
            "service": "https://public.api.bsky.app/",
          },
          "did": undefined,
        },
        "needsPersist": true,
      }
    `)
  })

  it('replaces local accounts with synced accounts', () => {
    let state = getInitialState([])

    const agent1 = new BskyAgent({service: 'https://alice.com'})
    agent1.sessionManager.session = {
      active: true,
      did: 'alice-did',
      handle: 'alice.test',
      accessJwt: 'alice-access-jwt-1',
      refreshJwt: 'alice-refresh-jwt-1',
    }
    const agent2 = new BskyAgent({service: 'https://bob.com'})
    agent2.sessionManager.session = {
      active: true,
      did: 'bob-did',
      handle: 'bob.test',
      accessJwt: 'bob-access-jwt-1',
      refreshJwt: 'bob-refresh-jwt-1',
    }
    state = run(state, [
      {
        type: 'switched-to-account',
        newAgent: agent1,
        newAccount: agentToSessionAccountOrThrow(agent1),
      },
      {
        type: 'switched-to-account',
        newAgent: agent2,
        newAccount: agentToSessionAccountOrThrow(agent2),
      },
    ])
    expect(state.accounts.length).toBe(2)
    expect(state.currentAgentState.did).toBe('bob-did')

    const anotherTabAgent1 = new BskyAgent({service: 'https://jay.com'})
    anotherTabAgent1.sessionManager.session = {
      active: true,
      did: 'jay-did',
      handle: 'jay.test',
      accessJwt: 'jay-access-jwt-1',
      refreshJwt: 'jay-refresh-jwt-1',
    }
    const anotherTabAgent2 = new BskyAgent({service: 'https://alice.com'})
    anotherTabAgent2.sessionManager.session = {
      active: true,
      did: 'bob-did',
      handle: 'bob.test',
      accessJwt: 'bob-access-jwt-2',
      refreshJwt: 'bob-refresh-jwt-2',
    }
    state = run(state, [
      {
        type: 'synced-accounts',
        syncedAccounts: [
          agentToSessionAccountOrThrow(anotherTabAgent1),
          agentToSessionAccountOrThrow(anotherTabAgent2),
        ],
        syncedCurrentDid: 'bob-did',
      },
    ])
    expect(state.accounts.length).toBe(2)
    expect(state.accounts[0].did).toBe('jay-did')
    expect(state.accounts[1].did).toBe('bob-did')
    expect(state.accounts[1].accessJwt).toBe('bob-access-jwt-2')
    // Keep Bob logged in.
    // (We patch up agent.session outside the reducer for this to work.)
    expect(state.currentAgentState.did).toBe('bob-did')
    expect(state.needsPersist).toBe(false)
    expect(printState(state)).toMatchInlineSnapshot(`
      {
        "accounts": [
          {
            "accessJwt": "jay-access-jwt-1",
            "active": true,
            "did": "jay-did",
            "email": undefined,
            "emailAuthFactor": false,
            "emailConfirmed": false,
            "handle": "jay.test",
            "isSelfHosted": true,
            "pdsUrl": undefined,
            "refreshJwt": "jay-refresh-jwt-1",
            "service": "https://jay.com/",
            "signupQueued": false,
            "status": undefined,
          },
          {
            "accessJwt": "bob-access-jwt-2",
            "active": true,
            "did": "bob-did",
            "email": undefined,
            "emailAuthFactor": false,
            "emailConfirmed": false,
            "handle": "bob.test",
            "isSelfHosted": true,
            "pdsUrl": undefined,
            "refreshJwt": "bob-refresh-jwt-2",
            "service": "https://alice.com/",
            "signupQueued": false,
            "status": undefined,
          },
        ],
        "currentAgentState": {
          "agent": {
            "service": "https://bob.com/",
          },
          "did": "bob-did",
        },
        "needsPersist": false,
      }
    `)

    const anotherTabAgent3 = new BskyAgent({service: 'https://clarence.com'})
    anotherTabAgent3.sessionManager.session = {
      active: true,
      did: 'clarence-did',
      handle: 'clarence.test',
      accessJwt: 'clarence-access-jwt-2',
      refreshJwt: 'clarence-refresh-jwt-2',
    }
    state = run(state, [
      {
        type: 'synced-accounts',
        syncedAccounts: [agentToSessionAccountOrThrow(anotherTabAgent3)],
        syncedCurrentDid: 'clarence-did',
      },
    ])
    expect(state.accounts.length).toBe(1)
    expect(state.accounts[0].did).toBe('clarence-did')
    // Log out because we have no matching user.
    // (In practice, we'll resume this session outside the reducer.)
    expect(state.currentAgentState.did).toBe(undefined)
    expect(state.needsPersist).toBe(false)
    expect(printState(state)).toMatchInlineSnapshot(`
      {
        "accounts": [
          {
            "accessJwt": "clarence-access-jwt-2",
            "active": true,
            "did": "clarence-did",
            "email": undefined,
            "emailAuthFactor": false,
            "emailConfirmed": false,
            "handle": "clarence.test",
            "isSelfHosted": true,
            "pdsUrl": undefined,
            "refreshJwt": "clarence-refresh-jwt-2",
            "service": "https://clarence.com/",
            "signupQueued": false,
            "status": undefined,
          },
        ],
        "currentAgentState": {
          "agent": {
            "service": "https://public.api.bsky.app/",
          },
          "did": undefined,
        },
        "needsPersist": false,
      }
    `)
  })
})

function run(initialState: State, actions: Action[]): State {
  let state = initialState
  for (let action of actions) {
    state = reducer(state, action)
  }
  return state
}

function printState(state: State) {
  return {
    accounts: state.accounts,
    currentAgentState: {
      agent: {service: state.currentAgentState.agent.service},
      did: state.currentAgentState.did,
    },
    needsPersist: state.needsPersist,
  }
}
