import SwiftUI
import Combine

extension Notification.Name {
    static let tokenExpired = Notification.Name("cloud.longde.AIStudyApp.tokenExpired")
}

enum AppSession: Equatable {
    case unknown
    case unauthenticated
    case authenticating
    case authenticated
    case refreshing
    case expired
    case disabled
    case deleted
}

@MainActor
final class AuthManager: ObservableObject {
    @Published var session: AppSession = .unknown

    var isLoading: Bool { session == .unknown }
    var isLoggedIn: Bool { session == .authenticated }

    static let shared = AuthManager()

    private var tokenExpiredObserver: NSObjectProtocol?

    init() {
        tokenExpiredObserver = NotificationCenter.default.addObserver(
            forName: .tokenExpired, object: nil, queue: .main
        ) { [weak self] notification in
            let errorCode = notification.userInfo?["errorCode"] as? String
            Task { @MainActor [weak self] in
                self?.handleUnauthorized(errorCode: errorCode)
            }
        }
    }

    deinit {
        if let observer = tokenExpiredObserver {
            NotificationCenter.default.removeObserver(observer)
        }
    }

    // MARK: - Session restore

    func restoreSession() async {
        session = .unknown

        guard let token = KeychainHelper.getAccessToken() else {
            session = .unauthenticated
            return
        }

        await APIClient.shared.setToken(token)

        do {
            let _: UserProfileResponse = try await APIClient.shared.request("/users/me")
            session = .authenticated
        } catch {
            // 如果 /users/me 返回了特定错误码（禁用/删除），直接切状态，不尝试 refresh
            if let apiError = error as? APIError, let code = apiError.errorCode {
                await APIClient.shared.setToken(nil)
                KeychainHelper.clear()
                applyErrorCode(code)
                return
            }

            session = .refreshing
            if let refreshed = await tryRefresh() {
                await APIClient.shared.setToken(refreshed.accessToken)
                KeychainHelper.save(
                    accessToken: refreshed.accessToken,
                    refreshToken: refreshed.refreshToken,
                    userId: refreshed.user?.id ?? ""
                )
                session = .authenticated
            } else {
                await APIClient.shared.setToken(nil)
                KeychainHelper.clear()
                session = .expired
            }
        }
    }

    // MARK: - Sign in

    func signIn(_ response: AuthResponse) async {
        session = .authenticating
        await APIClient.shared.setToken(response.accessToken)
        KeychainHelper.save(
            accessToken: response.accessToken,
            refreshToken: response.refreshToken,
            userId: response.user?.id ?? ""
        )
        session = .authenticated
    }

    // MARK: - Sign out

    func signOut() async {
        if let refreshToken = KeychainHelper.getRefreshToken() {
            let body = RefreshRequest(refreshToken: refreshToken)
            let _: GenericSuccessResponse? = try? await APIClient.shared.request(
                "/auth/logout", method: "POST", body: body
            )
        }
        await APIClient.shared.setToken(nil)
        KeychainHelper.clear()
        session = .unauthenticated
    }

    // MARK: - Force session update from error code

    func applyErrorCode(_ code: String?) {
        switch code {
        case "AUTH_USER_DISABLED":
            session = .disabled
        case "AUTH_USER_DELETED":
            session = .deleted
        case "AUTH_REFRESH_TOKEN_EXPIRED", "AUTH_REFRESH_TOKEN_REVOKED":
            session = .expired
        default:
            break
        }
    }

    // MARK: - Private

    private func handleUnauthorized(errorCode: String? = nil) {
        Task {
            await APIClient.shared.setToken(nil)
            KeychainHelper.clear()
            if let code = errorCode {
                applyErrorCode(code)
            } else {
                session = .unauthenticated
            }
        }
    }

    private func tryRefresh() async -> AuthResponse? {
        guard let refreshToken = KeychainHelper.getRefreshToken() else { return nil }
        do {
            let body = RefreshRequest(refreshToken: refreshToken)
            return try await APIClient.shared.request("/auth/refresh", method: "POST", body: body)
        } catch {
            return nil
        }
    }
}
