import Foundation import Combine /// Manages authentication state, user session, profile management, and audio sync access authorization. final class AuthManager: ObservableObject { static let shared = AuthManager() @Published private(set) var currentUser: UserAccount? @Published var isProcessing: Bool = false @Published var authErrorMessage: String? @Published var isSyncing: Bool = false @Published var lastSyncDate: Date? @Published var syncErrorMessage: String? var isLoggedIn: Bool { currentUser != nil } /// Auth repository interface. Can be swapped for `RemoteAuthServiceStub()` or mock services. private let authService: AuthServiceProtocol init(authService: AuthServiceProtocol = LocalAuthRepository()) { self.authService = authService self.currentUser = authService.getCurrentUser() } // MARK: - Auth Actions /// User login via username/email/phone & password. func login(identifier: String, password: String, completion: ((Bool, String?) -> Void)? = nil) { isProcessing = true authErrorMessage = nil let req = LoginRequest(identifier: identifier, password: password) authService.login(request: req) { [weak self] result in DispatchQueue.main.async { guard let self = self else { return } self.isProcessing = false switch result { case .success(let user): self.currentUser = user completion?(true, nil) case .failure(let error): self.authErrorMessage = error.localizedDescription completion?(false, error.localizedDescription) } } } } /// User registration. func register(username: String, identifier: String, password: String, completion: ((Bool, String?) -> Void)? = nil) { isProcessing = true authErrorMessage = nil let req = RegisterRequest(username: username, identifier: identifier, password: password) authService.register(request: req) { [weak self] result in DispatchQueue.main.async { guard let self = self else { return } self.isProcessing = false switch result { case .success(let user): self.currentUser = user completion?(true, nil) case .failure(let error): self.authErrorMessage = error.localizedDescription completion?(false, error.localizedDescription) } } } } /// Update profile information (nickname, email, phone, avatar). func updateProfile(username: String?, email: String?, phoneNumber: String?, avatarURL: String?, completion: ((Bool, String?) -> Void)? = nil) { isProcessing = true authErrorMessage = nil let req = UpdateProfileRequest(username: username, email: email, phoneNumber: phoneNumber, avatarURL: avatarURL) authService.updateProfile(request: req) { [weak self] result in DispatchQueue.main.async { guard let self = self else { return } self.isProcessing = false switch result { case .success(let user): self.currentUser = user completion?(true, nil) case .failure(let error): self.authErrorMessage = error.localizedDescription completion?(false, error.localizedDescription) } } } } /// Change password. func changePassword(oldPassword: String, newPassword: String, completion: ((Bool, String?) -> Void)? = nil) { isProcessing = true authErrorMessage = nil let req = ChangePasswordRequest(oldPassword: oldPassword, newPassword: newPassword) authService.changePassword(request: req) { [weak self] result in DispatchQueue.main.async { guard let self = self else { return } self.isProcessing = false switch result { case .success: completion?(true, nil) case .failure(let error): self.authErrorMessage = error.localizedDescription completion?(false, error.localizedDescription) } } } } /// Logout current user. func logout() { authService.logout { [weak self] in DispatchQueue.main.async { self?.currentUser = nil } } } // MARK: - Audio Sync /// Triggers audio synchronization. Requires authenticated user. func syncRecordings(onRequireAuth: @escaping () -> Void, onComplete: @escaping (Bool) -> Void) { guard isLoggedIn else { onRequireAuth() return } isSyncing = true syncErrorMessage = nil // Mock sync delay DispatchQueue.main.asyncAfter(deadline: .now() + 1.2) { [weak self] in guard let self = self else { return } self.isSyncing = false self.lastSyncDate = Date() onComplete(true) } } }