import CodexBarCore
import Foundation
import Testing
@testable import CodexBar

@Suite(.serialized)
@MainActor
struct ManagedCodexAccountServiceTests {
    @Test
    func `upsert preserves uuid for matching canonical email`() async throws {
        let root = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)
        defer { try? FileManager.default.removeItem(at: root) }

        let fileURL = root.appendingPathComponent("managed.json", isDirectory: false)
        let store = FileManagedCodexAccountStore(fileURL: fileURL, fileManager: .default)
        let service = ManagedCodexAccountService(
            store: store,
            homeFactory: TestManagedCodexHomeFactory(root: root),
            loginRunner: StubManagedCodexLoginRunner.success,
            identityReader: StubManagedCodexIdentityReader.accounts([
                .init(identity: .providerAccount(id: "account-live"), email: "user@example.com", plan: "Pro"),
                .init(identity: .providerAccount(id: "account-live"), email: "user@example.com", plan: "Pro"),
            ]),
            workspaceResolver: StubManagedCodexWorkspaceResolver())

        let first = try await service.authenticateManagedAccount()
        let second = try await service.authenticateManagedAccount()
        let snapshot = try store.loadAccounts()

        #expect(first.id == second.id)
        #expect(second.email == "user@example.com")
        #expect(second.providerAccountID == "account-live")
        #expect(snapshot.accounts.count == 1)
        #expect(second.managedHomePath.hasPrefix(root.standardizedFileURL.path + "/"))
    }

    @Test
    func `new authentication appends managed account without implicit selection side effect`() async throws {
        let root = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)
        defer { try? FileManager.default.removeItem(at: root) }

        let firstID = try #require(UUID(uuidString: "11111111-1111-1111-1111-111111111111"))
        let firstAccount = ManagedCodexAccount(
            id: firstID,
            email: "first@example.com",
            managedHomePath: root.appendingPathComponent("accounts/first", isDirectory: true).path,
            createdAt: 1,
            updatedAt: 1,
            lastAuthenticatedAt: 1)
        let store = InMemoryManagedCodexAccountStore(
            accounts: ManagedCodexAccountSet(
                version: 1,
                accounts: [firstAccount]))
        let service = ManagedCodexAccountService(
            store: store,
            homeFactory: TestManagedCodexHomeFactory(root: root),
            loginRunner: StubManagedCodexLoginRunner.success,
            identityReader: StubManagedCodexIdentityReader.accounts([
                .init(identity: .providerAccount(id: "account-second"), email: "second@example.com", plan: "Pro"),
            ]),
            workspaceResolver: StubManagedCodexWorkspaceResolver())

        let authenticated = try await service.authenticateManagedAccount()

        #expect(store.snapshot.accounts.count == 2)
        #expect(authenticated.email == "second@example.com")
        #expect(authenticated.providerAccountID == "account-second")
    }

    @Test
    func `same email provider backed workspaces coexist across sequential add account flows`() async throws {
        let root = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)
        defer { try? FileManager.default.removeItem(at: root) }

        let store = InMemoryManagedCodexAccountStore(
            accounts: ManagedCodexAccountSet(
                version: FileManagedCodexAccountStore.currentVersion,
                accounts: []))
        let service = ManagedCodexAccountService(
            store: store,
            homeFactory: TestManagedCodexHomeFactory(root: root),
            loginRunner: StubManagedCodexLoginRunner.success,
            identityReader: StubManagedCodexIdentityReader.accounts([
                .init(identity: .providerAccount(id: "workspace-personal"), email: "alice@example.com", plan: "Pro"),
                .init(identity: .providerAccount(id: "workspace-team"), email: "alice@example.com", plan: "Pro"),
            ]),
            workspaceResolver: StubManagedCodexWorkspaceResolver(identities: [
                "workspace-personal": CodexOpenAIWorkspaceIdentity(
                    workspaceAccountID: "workspace-personal",
                    workspaceLabel: "Personal"),
                "workspace-team": CodexOpenAIWorkspaceIdentity(
                    workspaceAccountID: "workspace-team",
                    workspaceLabel: "Team"),
            ]))

        let personal = try await service.authenticateManagedAccount()
        let team = try await service.authenticateManagedAccount()

        let storedPersonal = try #require(
            store.snapshot.account(email: "alice@example.com", providerAccountID: "workspace-personal"))
        let storedTeam = try #require(
            store.snapshot.account(email: "alice@example.com", providerAccountID: "workspace-team"))
        #expect(store.snapshot.accounts.count == 2)
        #expect(personal.id == storedPersonal.id)
        #expect(team.id == storedTeam.id)
        #expect(personal.id != team.id)
        #expect(storedPersonal.providerAccountID == "workspace-personal")
        #expect(storedPersonal.workspaceLabel == "Personal")
        #expect(storedTeam.providerAccountID == "workspace-team")
        #expect(storedTeam.workspaceLabel == "Team")
        #expect(storedPersonal.managedHomePath != storedTeam.managedHomePath)
        #expect(FileManager.default.fileExists(atPath: storedPersonal.managedHomePath))
        #expect(FileManager.default.fileExists(atPath: storedTeam.managedHomePath))
    }

    @Test
    func `same workspace provider id with different emails does not overwrite existing account`() async throws {
        let root = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)
        let existingHome = root.appendingPathComponent("accounts/existing", isDirectory: true)
        try FileManager.default.createDirectory(at: existingHome, withIntermediateDirectories: true)
        defer { try? FileManager.default.removeItem(at: root) }

        let existingID = try #require(UUID(uuidString: "10101010-2020-3030-4040-505050505050"))
        let existingAccount = ManagedCodexAccount(
            id: existingID,
            email: "mi.chaelfmk5542@gmail.com",
            providerAccountID: "team-4107",
            workspaceLabel: "4107",
            workspaceAccountID: "team-4107",
            managedHomePath: existingHome.path,
            createdAt: 1,
            updatedAt: 1,
            lastAuthenticatedAt: 1)
        let store = InMemoryManagedCodexAccountStore(
            accounts: ManagedCodexAccountSet(
                version: FileManagedCodexAccountStore.currentVersion,
                accounts: [existingAccount]))
        let service = ManagedCodexAccountService(
            store: store,
            homeFactory: TestManagedCodexHomeFactory(root: root),
            loginRunner: StubManagedCodexLoginRunner.success,
            identityReader: StubManagedCodexIdentityReader.accounts([
                .init(identity: .providerAccount(id: "team-4107"), email: "mich.aelfmk5542@gmail.com", plan: "Team"),
            ]),
            workspaceResolver: StubManagedCodexWorkspaceResolver(identities: [
                "team-4107": CodexOpenAIWorkspaceIdentity(
                    workspaceAccountID: "team-4107",
                    workspaceLabel: "4107"),
            ]))

        let added = try await service.authenticateManagedAccount()

        let original = try #require(
            store.snapshot.account(email: "mi.chaelfmk5542@gmail.com", providerAccountID: "team-4107"))
        let newAccount = try #require(
            store.snapshot.account(email: "mich.aelfmk5542@gmail.com", providerAccountID: "team-4107"))
        #expect(store.snapshot.accounts.count == 2)
        #expect(original.id == existingID)
        #expect(original.managedHomePath == existingHome.path)
        #expect(newAccount.id == added.id)
        #expect(newAccount.id != existingID)
        #expect(newAccount.workspaceLabel == "4107")
        #expect(FileManager.default.fileExists(atPath: existingHome.path))
        #expect(FileManager.default.fileExists(atPath: newAccount.managedHomePath))
    }

    @Test
    func `selected workspace is persisted and used as account identity`() async throws {
        let root = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)
        defer { try? FileManager.default.removeItem(at: root) }

        let store = InMemoryManagedCodexAccountStore(
            accounts: ManagedCodexAccountSet(
                version: FileManagedCodexAccountStore.currentVersion,
                accounts: []))
        let workspaces = [
            CodexOpenAIWorkspaceIdentity(
                workspaceAccountID: "workspace-personal",
                workspaceLabel: "Personal"),
            CodexOpenAIWorkspaceIdentity(
                workspaceAccountID: "workspace-team",
                workspaceLabel: "Team"),
        ]
        let service = ManagedCodexAccountService(
            store: store,
            homeFactory: TestManagedCodexHomeFactory(root: root),
            loginRunner: WritingManagedCodexLoginRunner(
                credentials: CodexOAuthCredentials(
                    accessToken: "access-token",
                    refreshToken: "refresh-token",
                    idToken: nil,
                    accountId: "workspace-personal",
                    lastRefresh: nil)),
            identityReader: StubManagedCodexIdentityReader.accounts([
                .init(identity: .providerAccount(id: "workspace-personal"), email: "alice@example.com", plan: "Pro"),
            ]),
            workspaceResolver: StubManagedCodexWorkspaceResolver(
                identities: Dictionary(uniqueKeysWithValues: workspaces.map { ($0.workspaceAccountID, $0) }),
                availableIdentities: workspaces),
            workspaceSelector: StubManagedCodexWorkspaceSelector(selectedWorkspaceID: "workspace-team"))

        let account = try await service.authenticateManagedAccount()
        let credentials = try CodexOAuthCredentialsStore.load(env: ["CODEX_HOME": account.managedHomePath])

        #expect(account.providerAccountID == "workspace-team")
        #expect(account.workspaceLabel == "Team")
        #expect(credentials.accountId == "workspace-team")
        #expect(store.snapshot.accounts.count == 1)
    }

    @Test
    func `reauth keeps previous home when store write fails`() async throws {
        let root = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)
        let existingHome = root.appendingPathComponent("accounts/existing", isDirectory: true)
        try FileManager.default.createDirectory(at: existingHome, withIntermediateDirectories: true)
        defer { try? FileManager.default.removeItem(at: root) }

        let existingAccountID = try #require(UUID(uuidString: "11111111-2222-3333-4444-555555555555"))
        let existingAccount = ManagedCodexAccount(
            id: existingAccountID,
            email: "user@example.com",
            managedHomePath: existingHome.path,
            createdAt: 1,
            updatedAt: 1,
            lastAuthenticatedAt: 1)
        let store = FailingManagedCodexAccountStore(
            accounts: ManagedCodexAccountSet(
                version: 1,
                accounts: [existingAccount]))
        let service = ManagedCodexAccountService(
            store: store,
            homeFactory: TestManagedCodexHomeFactory(root: root),
            loginRunner: StubManagedCodexLoginRunner.success,
            identityReader: StubManagedCodexIdentityReader.accounts([
                .init(identity: .providerAccount(id: "account-live"), email: "user@example.com", plan: "Pro"),
            ]),
            workspaceResolver: StubManagedCodexWorkspaceResolver())

        await #expect(throws: TestManagedCodexAccountStoreError.writeFailed) {
            try await service.authenticateManagedAccount()
        }

        let newHome = root.appendingPathComponent("accounts/account-1", isDirectory: true)
        #expect(FileManager.default.fileExists(atPath: existingHome.path))
        #expect(FileManager.default.fileExists(atPath: newHome.path) == false)
        #expect(store.snapshot.accounts.count == 1)
        #expect(store.snapshot.accounts.first?.managedHomePath == existingHome.path)
    }

    @Test
    func `reauth reconciles by provider account id before existing account id`() async throws {
        let root = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)
        let alphaHome = root.appendingPathComponent("accounts/alpha", isDirectory: true)
        let betaHome = root.appendingPathComponent("accounts/beta", isDirectory: true)
        try FileManager.default.createDirectory(at: alphaHome, withIntermediateDirectories: true)
        try FileManager.default.createDirectory(at: betaHome, withIntermediateDirectories: true)
        defer { try? FileManager.default.removeItem(at: root) }

        let alphaID = try #require(UUID(uuidString: "AAAAAAAA-BBBB-CCCC-DDDD-111111111111"))
        let betaID = try #require(UUID(uuidString: "BBBBBBBB-CCCC-DDDD-EEEE-222222222222"))
        let alphaAccount = ManagedCodexAccount(
            id: alphaID,
            email: "shared@example.com",
            providerAccountID: "account-alpha",
            managedHomePath: alphaHome.path,
            createdAt: 1,
            updatedAt: 1,
            lastAuthenticatedAt: 1)
        let betaAccount = ManagedCodexAccount(
            id: betaID,
            email: "shared@example.com",
            providerAccountID: "account-beta",
            managedHomePath: betaHome.path,
            createdAt: 2,
            updatedAt: 2,
            lastAuthenticatedAt: 2)
        let store = InMemoryManagedCodexAccountStore(
            accounts: ManagedCodexAccountSet(
                version: 1,
                accounts: [alphaAccount, betaAccount]))
        let service = ManagedCodexAccountService(
            store: store,
            homeFactory: TestManagedCodexHomeFactory(root: root),
            loginRunner: StubManagedCodexLoginRunner.success,
            identityReader: StubManagedCodexIdentityReader.accounts([
                .init(identity: .providerAccount(id: "account-beta"), email: "SHARED@example.com", plan: "Pro"),
            ]),
            workspaceResolver: StubManagedCodexWorkspaceResolver())

        let account = try await service.authenticateManagedAccount(existingAccountID: alphaAccount.id)

        let storedAlpha = try #require(store.snapshot.account(id: alphaAccount.id))
        let storedBeta = try #require(store.snapshot.account(id: betaAccount.id))
        #expect(account.id == betaAccount.id)
        #expect(store.snapshot.accounts.count == 2)
        #expect(storedAlpha.email == "shared@example.com")
        #expect(storedAlpha.providerAccountID == "account-alpha")
        #expect(storedAlpha.managedHomePath == alphaHome.path)
        #expect(storedBeta.email == "shared@example.com")
        #expect(storedBeta.providerAccountID == "account-beta")
        #expect(storedBeta.managedHomePath.hasPrefix(root.standardizedFileURL.path + "/"))
        #expect(storedBeta.managedHomePath != betaHome.path)
        #expect(FileManager.default.fileExists(atPath: alphaHome.path))
        #expect(FileManager.default.fileExists(atPath: betaHome.path) == false)
        #expect(FileManager.default.fileExists(atPath: storedBeta.managedHomePath))
    }

    @Test
    func `reauth to different account does not overwrite existing account id match`() async throws {
        let root = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)
        let aliceHome = root.appendingPathComponent("accounts/alice", isDirectory: true)
        try FileManager.default.createDirectory(at: aliceHome, withIntermediateDirectories: true)
        defer { try? FileManager.default.removeItem(at: root) }

        let aliceID = try #require(UUID(uuidString: "12121212-3434-5656-7878-909090909090"))
        let aliceAccount = ManagedCodexAccount(
            id: aliceID,
            email: "alice@example.com",
            providerAccountID: "account-alice",
            managedHomePath: aliceHome.path,
            createdAt: 1,
            updatedAt: 1,
            lastAuthenticatedAt: 1)
        let store = InMemoryManagedCodexAccountStore(accounts: ManagedCodexAccountSet(
            version: FileManagedCodexAccountStore.currentVersion,
            accounts: [aliceAccount]))
        let service = ManagedCodexAccountService(
            store: store,
            homeFactory: TestManagedCodexHomeFactory(root: root),
            loginRunner: StubManagedCodexLoginRunner.success,
            identityReader: StubManagedCodexIdentityReader.accounts([
                .init(identity: .providerAccount(id: "account-bob"), email: "bob@example.com", plan: "Pro"),
            ]),
            workspaceResolver: StubManagedCodexWorkspaceResolver())

        let account = try await service.authenticateManagedAccount(existingAccountID: aliceID)

        let storedAlice = try #require(store.snapshot.account(id: aliceID))
        let storedBob = try #require(store.snapshot.account(email: "bob@example.com"))
        #expect(account.id != aliceID)
        #expect(account.id == storedBob.id)
        #expect(store.snapshot.accounts.count == 2)
        #expect(storedAlice.email == "alice@example.com")
        #expect(storedAlice.providerAccountID == "account-alice")
        #expect(storedAlice.managedHomePath == aliceHome.path)
        #expect(storedBob.email == "bob@example.com")
        #expect(storedBob.providerAccountID == "account-bob")
        #expect(storedBob.managedHomePath.hasPrefix(root.standardizedFileURL.path + "/"))
        #expect(storedBob.managedHomePath != aliceHome.path)
        #expect(FileManager.default.fileExists(atPath: aliceHome.path))
        #expect(FileManager.default.fileExists(atPath: storedBob.managedHomePath))
    }

    @Test
    func `reauth on same email different workspace does not overwrite selected workspace`() async throws {
        let root = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)
        let personalHome = root.appendingPathComponent("accounts/personal", isDirectory: true)
        try FileManager.default.createDirectory(at: personalHome, withIntermediateDirectories: true)
        defer { try? FileManager.default.removeItem(at: root) }

        let personalID = try #require(UUID(uuidString: "31313131-4242-5353-6464-757575757575"))
        let personalAccount = ManagedCodexAccount(
            id: personalID,
            email: "alice@example.com",
            providerAccountID: "workspace-personal",
            workspaceLabel: "Personal",
            workspaceAccountID: "workspace-personal",
            managedHomePath: personalHome.path,
            createdAt: 1,
            updatedAt: 1,
            lastAuthenticatedAt: 1)
        let store = InMemoryManagedCodexAccountStore(accounts: ManagedCodexAccountSet(
            version: FileManagedCodexAccountStore.currentVersion,
            accounts: [personalAccount]))
        let service = ManagedCodexAccountService(
            store: store,
            homeFactory: TestManagedCodexHomeFactory(root: root),
            loginRunner: StubManagedCodexLoginRunner.success,
            identityReader: StubManagedCodexIdentityReader.accounts([
                .init(identity: .providerAccount(id: "workspace-team"), email: "alice@example.com", plan: "Pro"),
            ]),
            workspaceResolver: StubManagedCodexWorkspaceResolver(identities: [
                "workspace-team": CodexOpenAIWorkspaceIdentity(
                    workspaceAccountID: "workspace-team",
                    workspaceLabel: "Team"),
            ]))

        let account = try await service.authenticateManagedAccount(existingAccountID: personalID)

        let storedPersonal = try #require(store.snapshot.account(id: personalID))
        let storedTeam = try #require(
            store.snapshot.account(email: "alice@example.com", providerAccountID: "workspace-team"))
        #expect(account.id == storedTeam.id)
        #expect(account.id != personalID)
        #expect(store.snapshot.accounts.count == 2)
        #expect(storedPersonal.providerAccountID == "workspace-personal")
        #expect(storedPersonal.workspaceLabel == "Personal")
        #expect(storedPersonal.managedHomePath == personalHome.path)
        #expect(storedTeam.providerAccountID == "workspace-team")
        #expect(storedTeam.workspaceLabel == "Team")
        #expect(storedTeam.managedHomePath != personalHome.path)
        #expect(FileManager.default.fileExists(atPath: personalHome.path))
        #expect(FileManager.default.fileExists(atPath: storedTeam.managedHomePath))
    }

    @Test
    func `legacy row collapses onto provider backed row when provider id resolves elsewhere`() async throws {
        let root = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)
        let legacyHome = root.appendingPathComponent("accounts/legacy", isDirectory: true)
        let providerHome = root.appendingPathComponent("accounts/provider", isDirectory: true)
        try FileManager.default.createDirectory(at: legacyHome, withIntermediateDirectories: true)
        try FileManager.default.createDirectory(at: providerHome, withIntermediateDirectories: true)
        defer { try? FileManager.default.removeItem(at: root) }

        let legacyID = try #require(UUID(uuidString: "AAAAAAAA-1111-2222-3333-444444444444"))
        let providerID = try #require(UUID(uuidString: "BBBBBBBB-1111-2222-3333-444444444444"))
        let legacyAccount = ManagedCodexAccount(
            id: legacyID,
            email: "shared@example.com",
            managedHomePath: legacyHome.path,
            createdAt: 1,
            updatedAt: 1,
            lastAuthenticatedAt: 1)
        let providerAccount = ManagedCodexAccount(
            id: providerID,
            email: "shared@example.com",
            providerAccountID: "account-real",
            managedHomePath: providerHome.path,
            createdAt: 2,
            updatedAt: 2,
            lastAuthenticatedAt: 2)
        let store = InMemoryManagedCodexAccountStore(accounts: ManagedCodexAccountSet(
            version: FileManagedCodexAccountStore.currentVersion,
            accounts: [legacyAccount, providerAccount]))
        let service = ManagedCodexAccountService(
            store: store,
            homeFactory: TestManagedCodexHomeFactory(root: root),
            loginRunner: StubManagedCodexLoginRunner.success,
            identityReader: StubManagedCodexIdentityReader.accounts([
                .init(identity: .providerAccount(id: "account-real"), email: "shared@example.com", plan: "Pro"),
            ]),
            workspaceResolver: StubManagedCodexWorkspaceResolver())

        let account = try await service.authenticateManagedAccount(existingAccountID: legacyAccount.id)

        #expect(account.id == providerAccount.id)
        #expect(store.snapshot.accounts.count == 1)
        #expect(store.snapshot.accounts.first?.id == providerAccount.id)
        #expect(store.snapshot.accounts.first?.providerAccountID == "account-real")
        #expect(FileManager.default.fileExists(atPath: legacyHome.path) == false)
        #expect(FileManager.default.fileExists(atPath: providerHome.path) == false)
        #expect(FileManager.default.fileExists(atPath: account.managedHomePath))
    }

    @Test
    func `fresh provider login removes stale legacy row without explicit existing account id`() async throws {
        let root = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)
        let legacyHome = root.appendingPathComponent("accounts/legacy", isDirectory: true)
        let providerHome = root.appendingPathComponent("accounts/provider", isDirectory: true)
        try FileManager.default.createDirectory(at: legacyHome, withIntermediateDirectories: true)
        try FileManager.default.createDirectory(at: providerHome, withIntermediateDirectories: true)
        defer { try? FileManager.default.removeItem(at: root) }

        let legacyID = try #require(UUID(uuidString: "CCCCCCCC-1111-2222-3333-444444444444"))
        let providerID = try #require(UUID(uuidString: "DDDDDDDD-1111-2222-3333-444444444444"))
        let legacyAccount = ManagedCodexAccount(
            id: legacyID,
            email: "shared@example.com",
            managedHomePath: legacyHome.path,
            createdAt: 1,
            updatedAt: 1,
            lastAuthenticatedAt: 1)
        let providerAccount = ManagedCodexAccount(
            id: providerID,
            email: "shared@example.com",
            providerAccountID: "account-real",
            managedHomePath: providerHome.path,
            createdAt: 2,
            updatedAt: 2,
            lastAuthenticatedAt: 2)
        let store = InMemoryManagedCodexAccountStore(accounts: ManagedCodexAccountSet(
            version: FileManagedCodexAccountStore.currentVersion,
            accounts: [legacyAccount, providerAccount]))
        let service = ManagedCodexAccountService(
            store: store,
            homeFactory: TestManagedCodexHomeFactory(root: root),
            loginRunner: StubManagedCodexLoginRunner.success,
            identityReader: StubManagedCodexIdentityReader.accounts([
                .init(identity: .providerAccount(id: "account-real"), email: "shared@example.com", plan: "Pro"),
            ]),
            workspaceResolver: StubManagedCodexWorkspaceResolver())

        let account = try await service.authenticateManagedAccount()

        #expect(account.id == providerAccount.id)
        #expect(store.snapshot.accounts.count == 1)
        #expect(store.snapshot.accounts.first?.id == providerAccount.id)
        #expect(FileManager.default.fileExists(atPath: legacyHome.path) == false)
        #expect(FileManager.default.fileExists(atPath: providerHome.path) == false)
        #expect(FileManager.default.fileExists(atPath: account.managedHomePath))
    }

    @Test
    func `authentication persists workspace metadata and tolerates missing workspace label`() async throws {
        let root = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)
        defer { try? FileManager.default.removeItem(at: root) }

        let store = InMemoryManagedCodexAccountStore(
            accounts: ManagedCodexAccountSet(version: FileManagedCodexAccountStore.currentVersion, accounts: []))
        let service = ManagedCodexAccountService(
            store: store,
            homeFactory: TestManagedCodexHomeFactory(root: root),
            loginRunner: StubManagedCodexLoginRunner.success,
            identityReader: StubManagedCodexIdentityReader.accounts([
                .init(identity: .providerAccount(id: "account-live"), email: "user@example.com", plan: "Pro"),
                .init(identity: .providerAccount(id: "account-fallback"), email: "fallback@example.com", plan: "Pro"),
            ]),
            workspaceResolver: StubManagedCodexWorkspaceResolver(identities: [
                "account-live": CodexOpenAIWorkspaceIdentity(
                    workspaceAccountID: "account-live",
                    workspaceLabel: "Team Alpha"),
                "account-fallback": CodexOpenAIWorkspaceIdentity(
                    workspaceAccountID: "account-fallback",
                    workspaceLabel: nil),
            ]))

        let labeled = try await service.authenticateManagedAccount()
        let fallback = try await service.authenticateManagedAccount()

        #expect(labeled.providerAccountID == "account-live")
        #expect(labeled.workspaceAccountID == "account-live")
        #expect(labeled.workspaceLabel == "Team Alpha")
        #expect(fallback.providerAccountID == "account-fallback")
        #expect(fallback.workspaceAccountID == "account-fallback")
        #expect(fallback.workspaceLabel == nil)
    }

    @Test
    func `reauth preserves stored provider metadata when refresh cannot resolve account id`() async throws {
        let root = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)
        let existingHome = root.appendingPathComponent("accounts/existing", isDirectory: true)
        try FileManager.default.createDirectory(at: existingHome, withIntermediateDirectories: true)
        defer { try? FileManager.default.removeItem(at: root) }

        let existingID = try #require(UUID(uuidString: "EEEEEEEE-1111-2222-3333-444444444444"))
        let existingAccount = ManagedCodexAccount(
            id: existingID,
            email: "user@example.com",
            providerAccountID: "account-live",
            workspaceLabel: "Team Alpha",
            workspaceAccountID: "account-live",
            managedHomePath: existingHome.path,
            createdAt: 1,
            updatedAt: 1,
            lastAuthenticatedAt: 1)
        let store = InMemoryManagedCodexAccountStore(accounts: ManagedCodexAccountSet(
            version: FileManagedCodexAccountStore.currentVersion,
            accounts: [existingAccount]))
        let service = ManagedCodexAccountService(
            store: store,
            homeFactory: TestManagedCodexHomeFactory(root: root),
            loginRunner: StubManagedCodexLoginRunner.success,
            identityReader: StubManagedCodexIdentityReader.accounts([
                .init(
                    identity: .emailOnly(normalizedEmail: "user@example.com"),
                    email: "user@example.com",
                    plan: "Pro"),
            ]),
            workspaceResolver: StubManagedCodexWorkspaceResolver())

        let account = try await service.authenticateManagedAccount(existingAccountID: existingID)
        let stored = try #require(store.snapshot.account(id: existingID))

        #expect(account.id == existingID)
        #expect(account.providerAccountID == "account-live")
        #expect(account.workspaceAccountID == "account-live")
        #expect(account.workspaceLabel == "Team Alpha")
        #expect(stored.providerAccountID == "account-live")
        #expect(stored.workspaceAccountID == "account-live")
        #expect(stored.workspaceLabel == "Team Alpha")
        #expect(FileManager.default.fileExists(atPath: existingHome.path) == false)
        #expect(FileManager.default.fileExists(atPath: account.managedHomePath))
    }

    @Test
    func `auth failure cleanup uses managed root safety check`() async throws {
        let root = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)
        let outsideHome = FileManager.default.temporaryDirectory.appendingPathComponent(
            UUID().uuidString,
            isDirectory: true)
        defer {
            try? FileManager.default.removeItem(at: root)
            try? FileManager.default.removeItem(at: outsideHome)
        }

        let store = InMemoryManagedCodexAccountStore(
            accounts: ManagedCodexAccountSet(version: 1, accounts: []))
        let service = ManagedCodexAccountService(
            store: store,
            homeFactory: UnsafeManagedCodexHomeFactory(root: root, homeURL: outsideHome),
            loginRunner: StubManagedCodexLoginRunner(
                result: CodexLoginRunner.Result(outcome: .failed(status: 1), output: "nope")),
            identityReader: StubManagedCodexIdentityReader.emails([]),
            workspaceResolver: StubManagedCodexWorkspaceResolver())

        await #expect(throws: ManagedCodexAccountServiceError.self) {
            try await service.authenticateManagedAccount()
        }

        #expect(FileManager.default.fileExists(atPath: outsideHome.path))
        #expect(store.snapshot.accounts.isEmpty)
    }

    @Test
    func `auth failure preserves codex login result output`() async throws {
        let root = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)
        defer { try? FileManager.default.removeItem(at: root) }

        let loginResult = CodexLoginRunner.Result(
            outcome: .failed(status: 42),
            output: "OAuth callback used the wrong browser profile")
        let service = ManagedCodexAccountService(
            store: InMemoryManagedCodexAccountStore(
                accounts: ManagedCodexAccountSet(version: 1, accounts: [])),
            homeFactory: TestManagedCodexHomeFactory(root: root),
            loginRunner: StubManagedCodexLoginRunner(result: loginResult),
            identityReader: StubManagedCodexIdentityReader.emails([]),
            workspaceResolver: StubManagedCodexWorkspaceResolver())

        do {
            _ = try await service.authenticateManagedAccount()
            Issue.record("Expected managed Codex login failure")
        } catch let error as ManagedCodexAccountServiceError {
            guard case let .loginFailed(capturedResult) = error else {
                Issue.record("Expected loginFailed, got \(error)")
                return
            }
            #expect(capturedResult == loginResult)
            #expect(error.userFacingMessage.contains("codex --version"))
            #expect(error.userFacingMessage.contains("OAuth callback used the wrong browser profile"))
        }
    }

    @Test
    func `remove deletes managed home under managed root`() async throws {
        let root = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)
        let home = root.appendingPathComponent("accounts/account-a", isDirectory: true)
        try FileManager.default.createDirectory(at: home, withIntermediateDirectories: true)
        defer { try? FileManager.default.removeItem(at: root) }

        let accountID = try #require(UUID(uuidString: "AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE"))
        let account = ManagedCodexAccount(
            id: accountID,
            email: "user@example.com",
            managedHomePath: home.path,
            createdAt: 1,
            updatedAt: 1,
            lastAuthenticatedAt: 1)
        let store = InMemoryManagedCodexAccountStore(
            accounts: ManagedCodexAccountSet(version: 1, accounts: [account]))
        let service = ManagedCodexAccountService(
            store: store,
            homeFactory: TestManagedCodexHomeFactory(root: root),
            loginRunner: StubManagedCodexLoginRunner.success,
            identityReader: StubManagedCodexIdentityReader.emails([]),
            workspaceResolver: StubManagedCodexWorkspaceResolver())

        try await service.removeManagedAccount(id: account.id)

        #expect(store.snapshot.accounts.isEmpty)
        #expect(FileManager.default.fileExists(atPath: home.path) == false)
    }

    @Test
    func `remove keeps remaining managed account records`() async throws {
        let root = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)
        let firstHome = root.appendingPathComponent("accounts/account-a", isDirectory: true)
        let secondHome = root.appendingPathComponent("accounts/account-b", isDirectory: true)
        try FileManager.default.createDirectory(at: firstHome, withIntermediateDirectories: true)
        try FileManager.default.createDirectory(at: secondHome, withIntermediateDirectories: true)
        defer { try? FileManager.default.removeItem(at: root) }

        let firstID = try #require(UUID(uuidString: "AAAAAAAA-1111-1111-1111-111111111111"))
        let secondID = try #require(UUID(uuidString: "BBBBBBBB-2222-2222-2222-222222222222"))
        let first = ManagedCodexAccount(
            id: firstID,
            email: "first@example.com",
            managedHomePath: firstHome.path,
            createdAt: 1,
            updatedAt: 1,
            lastAuthenticatedAt: 1)
        let second = ManagedCodexAccount(
            id: secondID,
            email: "second@example.com",
            managedHomePath: secondHome.path,
            createdAt: 2,
            updatedAt: 2,
            lastAuthenticatedAt: 2)
        let store = InMemoryManagedCodexAccountStore(
            accounts: ManagedCodexAccountSet(
                version: 1,
                accounts: [first, second]))
        let service = ManagedCodexAccountService(
            store: store,
            homeFactory: TestManagedCodexHomeFactory(root: root),
            loginRunner: StubManagedCodexLoginRunner.success,
            identityReader: StubManagedCodexIdentityReader.emails([]),
            workspaceResolver: StubManagedCodexWorkspaceResolver())

        try await service.removeManagedAccount(id: second.id)

        #expect(store.snapshot.accounts.count == 1)
        #expect(store.snapshot.accounts.first?.id == first.id)
        #expect(FileManager.default.fileExists(atPath: secondHome.path) == false)
    }

    @Test
    func `remove keeps persisted account when store write fails`() async throws {
        let root = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)
        let home = root.appendingPathComponent("accounts/account-a", isDirectory: true)
        try FileManager.default.createDirectory(at: home, withIntermediateDirectories: true)
        defer { try? FileManager.default.removeItem(at: root) }

        let accountID = try #require(UUID(uuidString: "CCCCCCCC-DDDD-EEEE-FFFF-000000000000"))
        let account = ManagedCodexAccount(
            id: accountID,
            email: "user@example.com",
            managedHomePath: home.path,
            createdAt: 1,
            updatedAt: 1,
            lastAuthenticatedAt: 1)
        let store = FailingManagedCodexAccountStore(
            accounts: ManagedCodexAccountSet(version: 1, accounts: [account]))
        let service = ManagedCodexAccountService(
            store: store,
            homeFactory: TestManagedCodexHomeFactory(root: root),
            loginRunner: StubManagedCodexLoginRunner.success,
            identityReader: StubManagedCodexIdentityReader.emails([]),
            workspaceResolver: StubManagedCodexWorkspaceResolver())

        await #expect(throws: TestManagedCodexAccountStoreError.writeFailed) {
            try await service.removeManagedAccount(id: account.id)
        }

        #expect(store.snapshot.accounts.count == 1)
        #expect(store.snapshot.accounts.first?.managedHomePath == home.path)
        #expect(FileManager.default.fileExists(atPath: home.path))
    }

    @Test
    func `remove drops account but leaves unsafe home untouched`() async throws {
        let root = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)
        let outsideRoot = FileManager.default.temporaryDirectory.appendingPathComponent(
            UUID().uuidString,
            isDirectory: true)
        try FileManager.default.createDirectory(at: outsideRoot, withIntermediateDirectories: true)
        defer {
            try? FileManager.default.removeItem(at: root)
            try? FileManager.default.removeItem(at: outsideRoot)
        }

        let accountID = try #require(UUID(uuidString: "BBBBBBBB-CCCC-DDDD-EEEE-FFFFFFFFFFFF"))
        let account = ManagedCodexAccount(
            id: accountID,
            email: "user@example.com",
            managedHomePath: outsideRoot.path,
            createdAt: 1,
            updatedAt: 1,
            lastAuthenticatedAt: 1)
        let store = InMemoryManagedCodexAccountStore(
            accounts: ManagedCodexAccountSet(version: 1, accounts: [account]))
        let service = ManagedCodexAccountService(
            store: store,
            homeFactory: TestManagedCodexHomeFactory(root: root),
            loginRunner: StubManagedCodexLoginRunner.success,
            identityReader: StubManagedCodexIdentityReader.emails([]),
            workspaceResolver: StubManagedCodexWorkspaceResolver())

        try await service.removeManagedAccount(id: account.id)

        #expect(store.snapshot.accounts.isEmpty)
        #expect(FileManager.default.fileExists(atPath: outsideRoot.path))
    }
}

private final class InMemoryManagedCodexAccountStore: ManagedCodexAccountStoring, @unchecked Sendable {
    var snapshot: ManagedCodexAccountSet

    init(accounts: ManagedCodexAccountSet) {
        self.snapshot = accounts
    }

    func loadAccounts() throws -> ManagedCodexAccountSet {
        self.snapshot
    }

    func storeAccounts(_ accounts: ManagedCodexAccountSet) throws {
        self.snapshot = accounts
    }

    func ensureFileExists() throws -> URL {
        FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
    }
}

private final class FailingManagedCodexAccountStore: ManagedCodexAccountStoring, @unchecked Sendable {
    var snapshot: ManagedCodexAccountSet

    init(accounts: ManagedCodexAccountSet) {
        self.snapshot = accounts
    }

    func loadAccounts() throws -> ManagedCodexAccountSet {
        self.snapshot
    }

    func storeAccounts(_ accounts: ManagedCodexAccountSet) throws {
        _ = accounts
        throw TestManagedCodexAccountStoreError.writeFailed
    }

    func ensureFileExists() throws -> URL {
        FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
    }
}

private final class TestManagedCodexHomeFactory: ManagedCodexHomeProducing, @unchecked Sendable {
    let root: URL
    private let lock = NSLock()
    private var index: Int = 0

    init(root: URL) {
        self.root = root
    }

    private func nextPathComponent() -> String {
        self.lock.lock()
        defer { self.lock.unlock() }
        self.index += 1
        return "accounts/account-\(self.index)"
    }

    func makeHomeURL() -> URL {
        self.root.appendingPathComponent(self.nextPathComponent(), isDirectory: true)
    }

    func validateManagedHomeForDeletion(_ url: URL) throws {
        try ManagedCodexHomeFactory(root: self.root).validateManagedHomeForDeletion(url)
    }
}

private struct UnsafeManagedCodexHomeFactory: ManagedCodexHomeProducing {
    let root: URL
    let homeURL: URL

    func makeHomeURL() -> URL {
        self.homeURL
    }

    func validateManagedHomeForDeletion(_ url: URL) throws {
        try ManagedCodexHomeFactory(root: self.root).validateManagedHomeForDeletion(url)
    }
}

private struct StubManagedCodexLoginRunner: ManagedCodexLoginRunning {
    let result: CodexLoginRunner.Result

    func run(homePath: String, timeout: TimeInterval) async -> CodexLoginRunner.Result {
        self.result
    }

    static let success = StubManagedCodexLoginRunner(
        result: CodexLoginRunner.Result(outcome: .success, output: "ok"))
}

private struct WritingManagedCodexLoginRunner: ManagedCodexLoginRunning {
    let credentials: CodexOAuthCredentials

    func run(homePath: String, timeout _: TimeInterval) async -> CodexLoginRunner.Result {
        do {
            try CodexOAuthCredentialsStore.save(self.credentials, env: ["CODEX_HOME": homePath])
            return CodexLoginRunner.Result(outcome: .success, output: "ok")
        } catch {
            return CodexLoginRunner.Result(outcome: .failed(status: 1), output: String(describing: error))
        }
    }
}

private enum TestManagedCodexAccountStoreError: Error, Equatable {
    case writeFailed
}

private final class StubManagedCodexIdentityReader: ManagedCodexIdentityReading, @unchecked Sendable {
    private let lock = NSLock()
    private var identities: [CodexAuthBackedAccount]

    init(identities: [CodexAuthBackedAccount]) {
        self.identities = identities
    }

    func loadAccountIdentity(homePath _: String) throws -> CodexAuthBackedAccount {
        self.lock.lock()
        defer { self.lock.unlock() }
        guard !self.identities.isEmpty else {
            return CodexAuthBackedAccount(identity: .unresolved, email: nil, plan: nil)
        }
        return self.identities.removeFirst()
    }

    static func emails(_ emails: [String]) -> StubManagedCodexIdentityReader {
        StubManagedCodexIdentityReader(identities: emails.map { email in
            CodexAuthBackedAccount(
                identity: CodexIdentityResolver.resolve(accountId: nil, email: email),
                email: email,
                plan: "Pro")
        })
    }

    static func accounts(_ accounts: [CodexAuthBackedAccount]) -> StubManagedCodexIdentityReader {
        StubManagedCodexIdentityReader(identities: accounts)
    }
}

private struct StubManagedCodexWorkspaceResolver: ManagedCodexWorkspaceResolving {
    let identities: [String: CodexOpenAIWorkspaceIdentity]
    let availableIdentities: [CodexOpenAIWorkspaceIdentity]

    init(
        identities: [String: CodexOpenAIWorkspaceIdentity] = [:],
        availableIdentities: [CodexOpenAIWorkspaceIdentity] = [])
    {
        self.identities = identities
        self.availableIdentities = availableIdentities
    }

    func resolveWorkspaceIdentity(
        homePath _: String,
        providerAccountID: String) async -> CodexOpenAIWorkspaceIdentity?
    {
        self.identities[providerAccountID]
    }

    func availableWorkspaceIdentities(homePath _: String) async -> [CodexOpenAIWorkspaceIdentity] {
        self.availableIdentities
    }
}

private struct StubManagedCodexWorkspaceSelector: ManagedCodexWorkspaceSelecting {
    let selectedWorkspaceID: String?

    func selectWorkspace(
        email _: String,
        currentWorkspaceID _: String?,
        workspaces: [CodexOpenAIWorkspaceIdentity]) async -> CodexOpenAIWorkspaceIdentity?
    {
        workspaces.first { $0.workspaceAccountID == self.selectedWorkspaceID }
    }
}
