#if os(macOS)
import Foundation
import SweetCookieKit
import WebKit

@MainActor
// swiftlint:disable:next type_body_length
public struct OpenAIDashboardBrowserCookieImporter {
    public struct FoundAccount: Sendable, Hashable {
        public let sourceLabel: String
        public let email: String

        public init(sourceLabel: String, email: String) {
            self.sourceLabel = sourceLabel
            self.email = email
        }
    }

    public enum ImportError: LocalizedError {
        case noCookiesFound
        case browserAccessDenied(details: String)
        case browserCookieLoadTimedOut(details: String)
        case dashboardStillRequiresLogin
        case noMatchingAccount(found: [FoundAccount])
        case manualCookieHeaderInvalid

        public var errorDescription: String? {
            switch self {
            case .noCookiesFound:
                return "No browser cookies found."
            case let .browserAccessDenied(details):
                return "Browser cookie access denied. \(details)"
            case let .browserCookieLoadTimedOut(details):
                return "Browser cookie loading timed out. \(details)"
            case .dashboardStillRequiresLogin:
                return "Browser cookies imported, but dashboard still requires login."
            case let .noMatchingAccount(found):
                if found.isEmpty { return "No matching OpenAI web session found in browsers." }
                let display = found
                    .sorted { lhs, rhs in
                        if lhs.sourceLabel == rhs.sourceLabel { return lhs.email < rhs.email }
                        return lhs.sourceLabel < rhs.sourceLabel
                    }
                    .map { "\($0.sourceLabel)=\($0.email)" }
                    .joined(separator: ", ")
                return "OpenAI web session does not match Codex account. Found: \(display)."
            case .manualCookieHeaderInvalid:
                return "Manual cookie header is missing a valid OpenAI session cookie."
            }
        }
    }

    public struct ImportResult: Sendable {
        public let sourceLabel: String
        public let cookieCount: Int
        public let signedInEmail: String?
        public let matchesCodexEmail: Bool

        public init(
            sourceLabel: String,
            cookieCount: Int,
            signedInEmail: String?,
            matchesCodexEmail: Bool)
        {
            self.sourceLabel = sourceLabel
            self.cookieCount = cookieCount
            self.signedInEmail = signedInEmail
            self.matchesCodexEmail = matchesCodexEmail
        }
    }

    public init(browserDetection: BrowserDetection) {
        self.browserDetection = browserDetection
    }

    private let browserDetection: BrowserDetection

    struct PersistentValidationTimeout: Error {}

    nonisolated static func shouldTrustVerifiedSession(afterPersistFailure error: Error) -> Bool {
        error is PersistentValidationTimeout
    }

    nonisolated static func persistentValidationFailure(_ error: Error) -> Error {
        let nsError = error as NSError
        guard nsError.domain == NSURLErrorDomain, nsError.code == NSURLErrorTimedOut else { return error }
        return PersistentValidationTimeout()
    }

    private struct ImportDiagnostics {
        var mismatches: [FoundAccount] = []
        var foundAnyCookies: Bool = false
        var foundUnknownEmail: Bool = false
        var accessDeniedHints: [String] = []
    }

    private struct ImportContext {
        let targetEmail: String?
        let allowAnyAccount: Bool
        let cacheScope: CookieHeaderCache.Scope?
        let deadline: Date?
    }

    private static let cookieDomains = ["chatgpt.com", "openai.com"]
    private nonisolated static let cookieClient = BrowserCookieClient()
    private static let cookieImportOrder: BrowserCookieImportOrder =
        ProviderDefaults.metadata[.codex]?.browserCookieOrder ?? Browser.defaultImportOrder

    private enum CandidateEvaluation {
        case match(candidate: Candidate, signedInEmail: String)
        case mismatch(candidate: Candidate, signedInEmail: String)
        case loggedIn(candidate: Candidate, signedInEmail: String)
        case unknown(candidate: Candidate)
        case loginRequired(candidate: Candidate)
    }

    public func importBestCookies(
        intoAccountEmail targetEmail: String?,
        allowAnyAccount: Bool = false,
        preferCachedCookieHeader: Bool = true,
        cacheScope: CookieHeaderCache.Scope? = nil,
        deadline: Date? = nil,
        logger: ((String) -> Void)? = nil) async throws -> ImportResult
    {
        let log: (String) -> Void = { message in
            logger?("[web] \(message)")
        }

        let targetEmail = targetEmail?.trimmingCharacters(in: .whitespacesAndNewlines)
        let normalizedTarget = targetEmail?.isEmpty == false ? targetEmail : nil
        let context = ImportContext(
            targetEmail: normalizedTarget,
            allowAnyAccount: allowAnyAccount,
            cacheScope: cacheScope,
            deadline: deadline)

        if normalizedTarget != nil {
            log("Codex email known; matching required.")
        } else {
            guard context.allowAnyAccount else {
                throw ImportError.noCookiesFound
            }
            log("Codex email unknown; importing any signed-in session.")
        }

        var diagnostics = ImportDiagnostics()

        if preferCachedCookieHeader {
            let cached = try await Self.runBoundedCookieCacheOperation(deadline: deadline) {
                CookieHeaderCache.loadSerialized(provider: .codex, scope: cacheScope)
            }
            if let cached,
               !cached.cookieHeader.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
            {
                log("Using cached cookie header from \(cached.sourceLabel)")
                do {
                    return try await self.importManualCookies(
                        cookieHeader: cached.cookieHeader,
                        intoAccountEmail: context.targetEmail,
                        allowAnyAccount: context.allowAnyAccount,
                        cacheScope: cacheScope,
                        deadline: deadline,
                        logger: log)
                } catch let error as ImportError {
                    switch error {
                    case .manualCookieHeaderInvalid, .noMatchingAccount, .dashboardStillRequiresLogin:
                        do {
                            _ = try await Self.runBoundedCookieCacheOperation(deadline: deadline) {
                                CookieHeaderCache.clear(provider: .codex, scope: cacheScope)
                            }
                        } catch {
                            log("Stale cookie cache cleanup did not finish before the web deadline.")
                        }
                    default:
                        throw error
                    }
                } catch {
                    throw error
                }
            }
        } else {
            log("Skipping cached cookie header; forcing fresh browser import")
        }

        for browser in Self.cookieImportOrder {
            guard let issue = self.browserDetection.cookieSourceProfileAccessIssue(browser) else { continue }
            let hint = Self.browserProfileAccessHint(for: browser, issue: issue)
            diagnostics.accessDeniedHints.append(hint)
            log(hint)
        }

        // Filter to cookie-eligible browsers to avoid unnecessary keychain prompts
        let installedBrowsers = Self.cookieImportOrder.cookieImportCandidates(using: self.browserDetection)
        for browserSource in installedBrowsers {
            _ = try Self.remainingTimeout(until: deadline)
            if let match = try await self.trySource(
                browserSource,
                context: context,
                deadline: deadline,
                log: log,
                diagnostics: &diagnostics)
            {
                return match
            }
        }
        _ = try Self.remainingTimeout(until: deadline)

        if !diagnostics.mismatches.isEmpty {
            let found = Array(Set(diagnostics.mismatches)).sorted { lhs, rhs in
                if lhs.sourceLabel == rhs.sourceLabel { return lhs.email < rhs.email }
                return lhs.sourceLabel < rhs.sourceLabel
            }
            log("No matching browser session found. Candidate count: \(found.count)")
            throw ImportError.noMatchingAccount(found: found)
        }

        if diagnostics.foundUnknownEmail || diagnostics.foundAnyCookies {
            log("No matching browser session found (email unknown).")
            throw ImportError.noMatchingAccount(found: [])
        }

        if !diagnostics.accessDeniedHints.isEmpty {
            let details = Array(Set(diagnostics.accessDeniedHints)).sorted().joined(separator: " ")
            log("Cookie access denied: \(details)")
            throw ImportError.browserAccessDenied(details: details)
        }

        throw ImportError.noCookiesFound
    }

    nonisolated static func browserProfileAccessHint(
        for browser: Browser,
        issue: BrowserProfileAccessIssue,
        processName: String = ProcessInfo.processInfo.processName,
        executablePath: String? = Bundle.main.executablePath) -> String
    {
        let failure = switch issue {
        case .accessDenied: "macOS denied"
        case .unreadable: "macOS could not read"
        }
        let executable = executablePath?.trimmingCharacters(in: .whitespacesAndNewlines)
        let target: String = if let executable, !executable.isEmpty {
            Self.fullDiskAccessTarget(processName: processName, executablePath: executable)
        } else {
            processName
        }
        return "\(failure) \(browser.displayName) profile access for \(target). " +
            "Grant that exact component Full Disk Access, then quit and reopen it."
    }

    private nonisolated static func fullDiskAccessTarget(processName: String, executablePath: String) -> String {
        guard processName != "CodexBarCLI",
              let appSuffix = executablePath.range(of: ".app/")
        else {
            return "\(processName) (\(executablePath))"
        }
        let appPath = executablePath[..<appSuffix.upperBound].dropLast()
        return "CodexBar.app (\(appPath))"
    }

    nonisolated static func browserCookieLoadTimeoutError(
        for browser: Browser,
        processName: String = ProcessInfo.processInfo.processName,
        executablePath: String? = Bundle.main.executablePath) -> ImportError
    {
        let executable = executablePath?.trimmingCharacters(in: .whitespacesAndNewlines)
        let target: String = if let executable, !executable.isEmpty {
            Self.fullDiskAccessTarget(processName: processName, executablePath: executable)
        } else {
            processName
        }
        let keychainHint = browser.usesKeychainForCookieDecryption
            ? " If a macOS Keychain prompt is waiting, approve it."
            : ""
        let details = "\(browser.displayName) did not finish before the web timeout for \(target)." + keychainHint +
            " If the timeout repeats, grant that exact component Full Disk Access, quit and reopen it, then retry."
        return .browserCookieLoadTimedOut(details: details)
    }

    public func importManualCookies(
        cookieHeader: String,
        intoAccountEmail targetEmail: String?,
        allowAnyAccount: Bool = false,
        cacheScope: CookieHeaderCache.Scope? = nil,
        deadline: Date? = nil,
        logger: ((String) -> Void)? = nil) async throws -> ImportResult
    {
        let log: (String) -> Void = { message in
            logger?("[web] \(message)")
        }
        let normalizedTarget = targetEmail?.trimmingCharacters(in: .whitespacesAndNewlines)
        let allowAnyAccount = allowAnyAccount || normalizedTarget == nil || normalizedTarget?.isEmpty == true

        guard let normalized = CookieHeaderNormalizer.normalize(cookieHeader) else {
            throw ImportError.manualCookieHeaderInvalid
        }
        let pairs = CookieHeaderNormalizer.pairs(from: normalized)
        guard !pairs.isEmpty else { throw ImportError.manualCookieHeaderInvalid }
        let cookies = self.cookies(from: pairs)
        guard !cookies.isEmpty else { throw ImportError.manualCookieHeaderInvalid }

        let candidate = Candidate(label: "Manual", cookies: cookies)
        switch try await self.evaluateCandidate(
            candidate,
            targetEmail: normalizedTarget,
            allowAnyAccount: allowAnyAccount,
            deadline: deadline,
            log: log)
        {
        case let .match(_, signedInEmail):
            return try await self.persistVerifiedCandidate(
                candidate: candidate,
                targetEmail: signedInEmail,
                cacheScope: cacheScope,
                deadline: deadline,
                logger: log)
        case let .loggedIn(_, signedInEmail):
            return try await self.persistVerifiedCandidate(
                candidate: candidate,
                targetEmail: signedInEmail,
                cacheScope: cacheScope,
                deadline: deadline,
                logger: log)
        case let .mismatch(_, signedInEmail):
            throw ImportError.noMatchingAccount(found: [FoundAccount(sourceLabel: "Manual", email: signedInEmail)])
        case .unknown:
            if allowAnyAccount {
                return try await self.persistToDefaultStore(
                    candidate: candidate,
                    deadline: deadline,
                    logger: log)
            }
            throw ImportError.noMatchingAccount(found: [])
        case .loginRequired:
            throw ImportError.manualCookieHeaderInvalid
        }
    }

    private func trySafari(
        context: ImportContext,
        deadline: Date?,
        log: @escaping (String) -> Void,
        diagnostics: inout ImportDiagnostics) async throws -> ImportResult?
    {
        // Safari first: avoids touching Keychain ("Chrome Safe Storage") when Safari already matches.
        do {
            let query = BrowserCookieQuery(domains: Self.cookieDomains)
            let sources: [BrowserCookieStoreRecords]
            do {
                sources = try await Self.runBoundedCookieLoad(deadline: deadline) {
                    try Self.cookieClient.codexBarRecords(matching: query, in: .safari)
                }
            } catch let error as URLError where error.code == .timedOut {
                let timeoutError = Self.browserCookieLoadTimeoutError(for: .safari)
                log(timeoutError.localizedDescription)
                throw timeoutError
            }
            _ = try Self.remainingTimeout(until: deadline)
            guard !sources.isEmpty else {
                log("Safari contained 0 matching records.")
                return nil
            }
            for source in sources {
                _ = try Self.remainingTimeout(until: deadline)
                let cookies = BrowserCookieClient.makeHTTPCookies(source.records, origin: query.origin)
                guard !cookies.isEmpty else {
                    log("\(source.label) produced 0 HTTPCookies.")
                    continue
                }

                diagnostics.foundAnyCookies = true
                log("Loaded \(cookies.count) cookies from \(source.label) (\(self.cookieSummary(cookies)))")
                let candidate = Candidate(label: source.label, cookies: cookies)
                if let match = try await self.applyCandidate(
                    candidate,
                    context: context,
                    deadline: deadline,
                    log: log,
                    diagnostics: &diagnostics)
                {
                    return match
                }
            }
            return nil
        } catch let error as ImportError {
            throw error
        } catch let error as URLError where error.code == .timedOut {
            throw error
        } catch let error as BrowserCookieError {
            BrowserCookieAccessGate.recordIfNeeded(error)
            if let hint = error.accessDeniedHint {
                diagnostics.accessDeniedHints.append(hint)
            }
            log("Safari cookie load failed: \(error.localizedDescription)")
            return nil
        } catch {
            log("Safari cookie load failed: \(error.localizedDescription)")
            return nil
        }
    }

    /// Generic cookie loader for any non-Safari browser (Chrome, Edge, Firefox, Brave, Arc, etc.).
    /// SweetCookieKit handles engine-specific decryption internally.
    private func tryBrowser(
        _ browser: Browser,
        context: ImportContext,
        deadline: Date?,
        log: @escaping (String) -> Void,
        diagnostics: inout ImportDiagnostics) async throws -> ImportResult?
    {
        do {
            let query = BrowserCookieQuery(domains: Self.cookieDomains)
            let sources: [BrowserCookieStoreRecords]
            do {
                sources = try await Self.runBoundedCookieLoad(deadline: deadline) {
                    try Self.cookieClient.codexBarRecords(matching: query, in: browser)
                }
            } catch let error as URLError where error.code == .timedOut {
                let timeoutError = Self.browserCookieLoadTimeoutError(for: browser)
                log(timeoutError.localizedDescription)
                throw timeoutError
            }
            _ = try Self.remainingTimeout(until: deadline)
            guard !sources.isEmpty else {
                log("\(browser.displayName) contained 0 matching records.")
                return nil
            }
            for source in sources {
                _ = try Self.remainingTimeout(until: deadline)
                let cookies = BrowserCookieClient.makeHTTPCookies(source.records, origin: query.origin)
                guard !cookies.isEmpty else {
                    log("\(source.label) produced 0 HTTPCookies.")
                    continue
                }
                diagnostics.foundAnyCookies = true
                log("Loaded \(cookies.count) cookies from \(source.label) (\(self.cookieSummary(cookies)))")
                let candidate = Candidate(label: source.label, cookies: cookies)
                if let match = try await self.applyCandidate(
                    candidate,
                    context: context,
                    deadline: deadline,
                    log: log,
                    diagnostics: &diagnostics)
                {
                    return match
                }
            }
            return nil
        } catch let error as ImportError {
            throw error
        } catch let error as URLError where error.code == .timedOut {
            throw error
        } catch let error as BrowserCookieError {
            BrowserCookieAccessGate.recordIfNeeded(error)
            if let hint = error.accessDeniedHint {
                diagnostics.accessDeniedHints.append(hint)
            }
            log("\(browser.displayName) cookie load failed: \(error.localizedDescription)")
            return nil
        } catch {
            log("\(browser.displayName) cookie load failed: \(error.localizedDescription)")
            return nil
        }
    }

    private func trySource(
        _ source: Browser,
        context: ImportContext,
        deadline: Date?,
        log: @escaping (String) -> Void,
        diagnostics: inout ImportDiagnostics) async throws -> ImportResult?
    {
        switch source {
        case .safari:
            try await self.trySafari(
                context: context,
                deadline: deadline,
                log: log,
                diagnostics: &diagnostics)
        default:
            // All non-Safari browsers (Chrome, Edge, Firefox, Brave, Arc, etc.)
            // share the same cookie loading path via SweetCookieKit.
            try await self.tryBrowser(
                source,
                context: context,
                deadline: deadline,
                log: log,
                diagnostics: &diagnostics)
        }
    }

    private func applyCandidate(
        _ candidate: Candidate,
        context: ImportContext,
        deadline: Date?,
        log: @escaping (String) -> Void,
        diagnostics: inout ImportDiagnostics) async throws -> ImportResult?
    {
        switch try await self.evaluateCandidate(
            candidate,
            targetEmail: context.targetEmail,
            allowAnyAccount: context.allowAnyAccount,
            deadline: deadline,
            log: log)
        {
        case let .match(candidate, signedInEmail):
            log("Selected \(candidate.label) (matches Codex: \(signedInEmail))")
            guard let targetEmail = context.targetEmail else { return nil }
            if let result = try? await self.persistVerifiedCandidate(
                candidate: candidate,
                targetEmail: targetEmail,
                cacheScope: context.cacheScope,
                deadline: deadline,
                logger: log)
            {
                await self.cacheCookies(
                    candidate: candidate,
                    scope: context.cacheScope,
                    deadline: deadline,
                    logger: log)
                return result
            }
            _ = try Self.remainingTimeout(until: deadline)
            return nil
        case let .mismatch(candidate, signedInEmail):
            try await self.handleMismatch(
                candidate: candidate,
                signedInEmail: signedInEmail,
                context: context,
                log: log,
                diagnostics: &diagnostics)
            _ = try Self.remainingTimeout(until: deadline)
            return nil
        case let .loggedIn(candidate, signedInEmail):
            log("Selected \(candidate.label) (signed in: \(signedInEmail))")
            if let result = try? await self.persistVerifiedCandidate(
                candidate: candidate,
                targetEmail: signedInEmail,
                cacheScope: context.cacheScope,
                deadline: deadline,
                logger: log)
            {
                await self.cacheCookies(
                    candidate: candidate,
                    scope: context.cacheScope,
                    deadline: deadline,
                    logger: log)
                return result
            }
            _ = try Self.remainingTimeout(until: deadline)
            return nil
        case .unknown:
            if context.allowAnyAccount {
                log("Selected \(candidate.label) (signed in: unknown)")
                if let result = try? await self.persistToDefaultStore(
                    candidate: candidate,
                    deadline: deadline,
                    logger: log)
                {
                    await self.cacheCookies(
                        candidate: candidate,
                        scope: context.cacheScope,
                        deadline: deadline,
                        logger: log)
                    return result
                }
                _ = try Self.remainingTimeout(until: deadline)
                return nil
            }
            diagnostics.foundUnknownEmail = true
            return nil
        case .loginRequired:
            return nil
        }
    }

    private func evaluateCandidate(
        _ candidate: Candidate,
        targetEmail: String?,
        allowAnyAccount: Bool,
        deadline: Date?,
        log: @escaping (String) -> Void) async throws -> CandidateEvaluation
    {
        log("Trying candidate \(candidate.label) (\(candidate.cookies.count) cookies)")

        let apiEmail = try await self.fetchSignedInEmailFromAPI(
            cookies: candidate.cookies,
            deadline: deadline,
            logger: log)
        if let apiEmail {
            log("Candidate \(candidate.label) API email: \(apiEmail)")
        }

        // Prefer the API email when available (fast; avoids WebKit hydration/timeout risks).
        if let apiEmail, !apiEmail.isEmpty {
            if let targetEmail {
                if apiEmail.lowercased() == targetEmail.lowercased() {
                    return .match(candidate: candidate, signedInEmail: apiEmail)
                }
                return .mismatch(candidate: candidate, signedInEmail: apiEmail)
            }
            if allowAnyAccount { return .loggedIn(candidate: candidate, signedInEmail: apiEmail) }
        }

        if !self.hasSessionCookies(candidate.cookies) {
            log("Candidate \(candidate.label) missing session cookies; skipping")
            return .loginRequired(candidate: candidate)
        }

        let scratch = WKWebsiteDataStore.nonPersistent()
        try await self.setCookies(candidate.cookies, into: scratch, deadline: deadline)

        do {
            let probe = try await OpenAIDashboardFetcher().probeUsagePage(
                websiteDataStore: scratch,
                logger: log,
                timeout: Self.remainingTimeout(until: deadline, cappedAt: 25))
            let signedInEmail = probe.signedInEmail?.trimmingCharacters(in: .whitespacesAndNewlines)
            log("Candidate \(candidate.label) DOM email: \(signedInEmail ?? "unknown")")

            let resolvedEmail = signedInEmail?.trimmingCharacters(in: .whitespacesAndNewlines)
            if let resolvedEmail, !resolvedEmail.isEmpty {
                if let targetEmail {
                    if resolvedEmail.lowercased() == targetEmail.lowercased() {
                        return .match(candidate: candidate, signedInEmail: resolvedEmail)
                    }
                    return .mismatch(candidate: candidate, signedInEmail: resolvedEmail)
                }
                if allowAnyAccount { return .loggedIn(candidate: candidate, signedInEmail: resolvedEmail) }
            }

            return .unknown(candidate: candidate)
        } catch OpenAIDashboardFetcher.FetchError.loginRequired {
            log("Candidate \(candidate.label) requires login.")
            return .loginRequired(candidate: candidate)
        } catch {
            _ = try Self.remainingTimeout(until: deadline)
            log("Candidate \(candidate.label) probe error: \(error.localizedDescription)")
            return .unknown(candidate: candidate)
        }
    }

    private func hasSessionCookies(_ cookies: [HTTPCookie]) -> Bool {
        for cookie in cookies {
            let name = cookie.name.lowercased()
            if name.contains("session-token") || name.contains("authjs") || name.contains("next-auth") {
                return true
            }
            if name == "_account" { return true }
        }
        return false
    }

    private func handleMismatch(
        candidate: Candidate,
        signedInEmail: String,
        context: ImportContext,
        log: @escaping (String) -> Void,
        diagnostics: inout ImportDiagnostics) async throws
    {
        log("Candidate \(candidate.label) mismatch (\(signedInEmail)); continuing browser search")
        diagnostics.mismatches.append(FoundAccount(sourceLabel: candidate.label, email: signedInEmail))
        // Mismatch still means we found a valid signed-in session. Keep it inside the active source scope;
        // a profile import must never populate the live account or another profile's WebKit store.
        do {
            try await self.persistCookies(
                candidate: candidate,
                accountEmail: signedInEmail,
                cacheScope: context.cacheScope,
                deadline: context.deadline,
                logger: log)
        } catch {
            log("Could not cache mismatched session: \(error.localizedDescription)")
            _ = try Self.remainingTimeout(until: context.deadline)
        }
    }

    func fetchSignedInEmailFromAPI(
        cookies: [HTTPCookie],
        deadline: Date?,
        logger: (String) -> Void) async throws -> String?
    {
        let chatgptCookies = cookies.filter { $0.domain.lowercased().contains("chatgpt.com") }
        guard !chatgptCookies.isEmpty else { return nil }

        let cookieHeader = chatgptCookies
            .map { "\($0.name)=\($0.value)" }
            .joined(separator: "; ")

        let endpoints = [
            "https://chatgpt.com/backend-api/me",
            "https://chatgpt.com/api/auth/session",
        ]

        for urlString in endpoints {
            let requestTimeout = try Self.remainingTimeout(until: deadline, cappedAt: 10)
            guard let url = URL(string: urlString) else { continue }
            let request = OpenAIDashboardFetcher.dashboardIdentityAPIRequest(
                url: url,
                cookieHeader: cookieHeader,
                timeout: requestTimeout)

            do {
                let (data, response) = try await CodexAuthenticatedHTTPTransport.current.data(for: request)
                let status = (response as? HTTPURLResponse)?.statusCode ?? -1
                logger("API \(url.host ?? "chatgpt.com") \(url.path) status=\(status)")
                guard status >= 200, status < 300 else { continue }
                if let email = Self.findFirstEmail(inJSONData: data) {
                    return email.trimmingCharacters(in: .whitespacesAndNewlines)
                }
            } catch {
                _ = try Self.remainingTimeout(until: deadline)
                logger("API request failed: \(error.localizedDescription)")
            }
        }

        return nil
    }

    private static func findFirstEmail(inJSONData data: Data) -> String? {
        guard let json = try? JSONSerialization.jsonObject(with: data, options: []) else { return nil }
        var queue: [Any] = [json]
        var seen = 0
        while !queue.isEmpty, seen < 2000 {
            let cur = queue.removeFirst()
            seen += 1
            if let str = cur as? String, str.contains("@") {
                return str
            }
            if let dict = cur as? [String: Any] {
                for (k, v) in dict {
                    if k.lowercased() == "email", let s = v as? String, s.contains("@") { return s }
                    queue.append(v)
                }
            } else if let arr = cur as? [Any] {
                queue.append(contentsOf: arr)
            }
        }
        return nil
    }

    private func persistVerifiedCandidate(
        candidate: Candidate,
        targetEmail: String,
        cacheScope: CookieHeaderCache.Scope?,
        deadline: Date?,
        logger: @escaping (String) -> Void) async throws -> ImportResult
    {
        do {
            return try await self.persist(
                candidate: candidate,
                targetEmail: targetEmail,
                cacheScope: cacheScope,
                deadline: deadline,
                logger: logger)
        } catch {
            guard Self.shouldTrustVerifiedSession(afterPersistFailure: error) else {
                throw error
            }

            let signedInEmail = targetEmail.trimmingCharacters(in: .whitespacesAndNewlines)
            logger(
                "Persistent validation timed out after session verification; " +
                    "keeping \(candidate.label) cookies for \(signedInEmail).")
            return ImportResult(
                sourceLabel: candidate.label,
                cookieCount: candidate.cookies.count,
                signedInEmail: signedInEmail,
                matchesCodexEmail: signedInEmail.lowercased() == targetEmail.lowercased())
        }
    }

    private func persist(
        candidate: Candidate,
        targetEmail: String,
        cacheScope: CookieHeaderCache.Scope?,
        deadline: Date?,
        logger: @escaping (String) -> Void) async throws -> ImportResult
    {
        let persistent = OpenAIDashboardWebsiteDataStore.store(
            forAccountEmail: targetEmail,
            scope: cacheScope)
        try await self.clearChatGPTCookies(in: persistent, deadline: deadline)
        try await self.setCookies(candidate.cookies, into: persistent, deadline: deadline)

        // Validate against the persistent store (login + email sync).
        do {
            let probe = try await OpenAIDashboardFetcher().probeUsagePage(
                websiteDataStore: persistent,
                logger: logger,
                timeout: Self.remainingTimeout(until: deadline, cappedAt: 20),
                preserveLoadedPageForReuse: true)
            let signed = probe.signedInEmail?.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
            let matches = signed?.lowercased() == targetEmail.lowercased()
            logger("Persistent session signed in as: \(signed ?? "unknown")")
            if signed != nil, matches == false {
                let found = signed?.isEmpty == false
                    ? [FoundAccount(sourceLabel: candidate.label, email: signed!)]
                    : []
                throw ImportError.noMatchingAccount(found: found)
            }
            return ImportResult(
                sourceLabel: candidate.label,
                cookieCount: candidate.cookies.count,
                signedInEmail: signed,
                matchesCodexEmail: matches)
        } catch OpenAIDashboardFetcher.FetchError.loginRequired {
            OpenAIDashboardWebViewCache.shared.evict(websiteDataStore: persistent)
            logger("Selected \(candidate.label) but dashboard still requires login.")
            throw ImportError.dashboardStillRequiresLogin
        } catch {
            OpenAIDashboardWebViewCache.shared.evict(websiteDataStore: persistent)
            throw Self.persistentValidationFailure(error)
        }
    }

    private func persistToDefaultStore(
        candidate: Candidate,
        deadline: Date?,
        logger: @escaping (String) -> Void) async throws -> ImportResult
    {
        let persistent = OpenAIDashboardWebsiteDataStore.store(forAccountEmail: nil)
        try await self.clearChatGPTCookies(in: persistent, deadline: deadline)
        try await self.setCookies(candidate.cookies, into: persistent, deadline: deadline)

        do {
            let probe = try await OpenAIDashboardFetcher().probeUsagePage(
                websiteDataStore: persistent,
                logger: logger,
                timeout: Self.remainingTimeout(until: deadline, cappedAt: 20),
                preserveLoadedPageForReuse: true)
            let signed = probe.signedInEmail?.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
            logger("Persistent session signed in as: \(signed ?? "unknown")")
            return ImportResult(
                sourceLabel: candidate.label,
                cookieCount: candidate.cookies.count,
                signedInEmail: signed,
                matchesCodexEmail: false)
        } catch OpenAIDashboardFetcher.FetchError.loginRequired {
            OpenAIDashboardWebViewCache.shared.evict(websiteDataStore: persistent)
            logger("Selected \(candidate.label) but dashboard still requires login.")
            throw ImportError.dashboardStillRequiresLogin
        } catch {
            OpenAIDashboardWebViewCache.shared.evict(websiteDataStore: persistent)
            throw error
        }
    }

    // MARK: - Candidates

    private func cookies(from pairs: [(name: String, value: String)]) -> [HTTPCookie] {
        var cookies: [HTTPCookie] = []
        for domain in Self.cookieDomains {
            for pair in pairs {
                let props: [HTTPCookiePropertyKey: Any] = [
                    .name: pair.name,
                    .value: pair.value,
                    .domain: domain,
                    .path: "/",
                    .secure: true,
                ]
                if let cookie = HTTPCookie(properties: props) {
                    cookies.append(cookie)
                }
            }
        }
        return cookies
    }

    private func cacheCookies(
        candidate: Candidate,
        scope: CookieHeaderCache.Scope?,
        deadline: Date?,
        logger: @escaping (String) -> Void) async
    {
        let header = self.cookieHeader(from: candidate.cookies)
        guard !header.isEmpty else { return }
        do {
            _ = try await Self.runBoundedCookieCacheOperation(deadline: deadline) {
                CookieHeaderCache.store(
                    provider: .codex,
                    scope: scope,
                    cookieHeader: header,
                    sourceLabel: candidate.label)
                return true
            }
        } catch {
            logger("Cookie cache write did not finish before the web deadline.")
        }
    }

    private func cookieHeader(from cookies: [HTTPCookie]) -> String {
        cookies.map { "\($0.name)=\($0.value)" }.joined(separator: "; ")
    }

    private struct Candidate {
        let label: String
        let cookies: [HTTPCookie]
    }

    // MARK: - WebKit cookie store

    private func persistCookies(
        candidate: Candidate,
        accountEmail: String,
        cacheScope: CookieHeaderCache.Scope?,
        deadline: Date?,
        logger: (String) -> Void) async throws
    {
        let store = OpenAIDashboardWebsiteDataStore.store(forAccountEmail: accountEmail, scope: cacheScope)
        try await self.clearChatGPTCookies(in: store, deadline: deadline)
        try await self.setCookies(candidate.cookies, into: store, deadline: deadline)
        logger("Persisted cookies for \(accountEmail) (source=\(candidate.label))")
    }

    private func clearChatGPTCookies(in store: WKWebsiteDataStore, deadline: Date?) async throws {
        try await Self.runSerializedCallback(
            key: ObjectIdentifier(store),
            deadline: deadline)
        { completion in
            store.fetchDataRecords(ofTypes: WKWebsiteDataStore.allWebsiteDataTypes()) { records in
                let filtered = records.filter { record in
                    let name = record.displayName.lowercased()
                    return name.contains("chatgpt.com") || name.contains("openai.com")
                }
                store.removeData(ofTypes: WKWebsiteDataStore.allWebsiteDataTypes(), for: filtered) {
                    completion()
                }
            }
        }
    }

    private func setCookies(
        _ cookies: [HTTPCookie],
        into store: WKWebsiteDataStore,
        deadline: Date?) async throws
    {
        for cookie in cookies {
            try await Self.runSerializedCallback(
                key: ObjectIdentifier(store),
                deadline: deadline)
            { completion in
                store.httpCookieStore.setCookie(cookie, completionHandler: completion)
            }
        }
    }

    private func cookieSummary(_ cookies: [HTTPCookie]) -> String {
        let nameCounts = Dictionary(grouping: cookies, by: \.name).mapValues { $0.count }
        let important = [
            "__Secure-next-auth.session-token",
            "__Secure-next-auth.session-token.0",
            "__Secure-next-auth.session-token.1",
            "_account",
            "oai-did",
            "cf_clearance",
        ]
        let parts: [String] = important.compactMap { name -> String? in
            guard let c = nameCounts[name], c > 0 else { return nil }
            return "\(name)=\(c)"
        }
        if parts.isEmpty { return "no key cookies detected" }
        return parts.joined(separator: ", ")
    }
}
#else
import Foundation

@MainActor
public struct OpenAIDashboardBrowserCookieImporter {
    public struct FoundAccount: Sendable, Hashable {
        public let sourceLabel: String
        public let email: String

        public init(sourceLabel: String, email: String) {
            self.sourceLabel = sourceLabel
            self.email = email
        }
    }

    public enum ImportError: LocalizedError {
        case noCookiesFound
        case browserAccessDenied(details: String)
        case browserCookieLoadTimedOut(details: String)
        case dashboardStillRequiresLogin
        case noMatchingAccount(found: [FoundAccount])
        case manualCookieHeaderInvalid

        public var errorDescription: String? {
            switch self {
            case .noCookiesFound:
                return "No browser cookies found."
            case let .browserAccessDenied(details):
                return "Browser cookie access denied. \(details)"
            case let .browserCookieLoadTimedOut(details):
                return "Browser cookie loading timed out. \(details)"
            case .dashboardStillRequiresLogin:
                return "Browser cookies imported, but dashboard still requires login."
            case let .noMatchingAccount(found):
                if found.isEmpty { return "No matching OpenAI web session found in browsers." }
                let display = found
                    .sorted { lhs, rhs in
                        if lhs.sourceLabel == rhs.sourceLabel { return lhs.email < rhs.email }
                        return lhs.sourceLabel < rhs.sourceLabel
                    }
                    .map { "\($0.sourceLabel)=\($0.email)" }
                    .joined(separator: ", ")
                return "OpenAI web session does not match Codex account. Found: \(display)."
            case .manualCookieHeaderInvalid:
                return "Manual cookie header is missing a valid OpenAI session cookie."
            }
        }
    }

    public struct ImportResult: Sendable {
        public let sourceLabel: String
        public let cookieCount: Int
        public let signedInEmail: String?
        public let matchesCodexEmail: Bool

        public init(sourceLabel: String, cookieCount: Int, signedInEmail: String?, matchesCodexEmail: Bool) {
            self.sourceLabel = sourceLabel
            self.cookieCount = cookieCount
            self.signedInEmail = signedInEmail
            self.matchesCodexEmail = matchesCodexEmail
        }
    }

    public init() {}

    public func importBestCookies(
        intoAccountEmail _: String?,
        allowAnyAccount _: Bool = false,
        preferCachedCookieHeader _: Bool = true,
        cacheScope _: CookieHeaderCache.Scope? = nil,
        deadline _: Date? = nil,
        logger _: ((String) -> Void)? = nil) async throws -> ImportResult
    {
        throw ImportError.browserAccessDenied(details: "OpenAI web cookie import is only supported on macOS.")
    }

    public func importManualCookies(
        cookieHeader _: String,
        intoAccountEmail _: String?,
        allowAnyAccount _: Bool = false,
        cacheScope _: CookieHeaderCache.Scope? = nil,
        deadline _: Date? = nil,
        logger _: ((String) -> Void)? = nil) async throws -> ImportResult
    {
        throw ImportError.browserAccessDenied(details: "OpenAI web cookie import is only supported on macOS.")
    }
}
#endif
