import Foundation
import Security

actor StorageService {
    nonisolated(unsafe) static let shared = StorageService()

    private init() {}

    // MARK: - Keychain Operations

    func saveCredentials(_ credentials: XStreamCredentials) throws {
        let encoder = JSONEncoder()
        guard let data = try? encoder.encode(credentials) else {
            throw NetworkError.encodingError
        }

        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrService as String: Constants.Storage.keychainService,
            kSecAttrAccount as String: Constants.Storage.credentialsKey,
            kSecValueData as String: data
        ]

        SecItemDelete(query as CFDictionary)

        let status = SecItemAdd(query as CFDictionary, nil)

        guard status == errSecSuccess else {
            throw NSError(
                domain: "StorageService",
                code: Int(status),
                userInfo: [NSLocalizedDescriptionKey: "Failed to save credentials to Keychain"]
            )
        }
    }

    func loadCredentials() throws -> XStreamCredentials? {
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrService as String: Constants.Storage.keychainService,
            kSecAttrAccount as String: Constants.Storage.credentialsKey,
            kSecReturnData as String: true,
            kSecMatchLimit as String: kSecMatchLimitOne
        ]

        var result: AnyObject?
        let status = SecItemCopyMatching(query as CFDictionary, &result)

        guard status == errSecSuccess else {
            if status == errSecItemNotFound {
                return nil
            }
            throw NSError(
                domain: "StorageService",
                code: Int(status),
                userInfo: [NSLocalizedDescriptionKey: "Failed to load credentials from Keychain"]
            )
        }

        guard let data = result as? Data else {
            return nil
        }

        let decoder = JSONDecoder()
        do {
            let credentials = try decoder.decode(XStreamCredentials.self, from: data)
            return credentials
        } catch {
            throw NetworkError.decodingError(error)
        }
    }

    func clearCredentials() throws {
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrService as String: Constants.Storage.keychainService,
            kSecAttrAccount as String: Constants.Storage.credentialsKey
        ]

        let status = SecItemDelete(query as CFDictionary)

        guard status == errSecSuccess || status == errSecItemNotFound else {
            throw NSError(
                domain: "StorageService",
                code: Int(status),
                userInfo: [NSLocalizedDescriptionKey: "Failed to delete credentials from Keychain"]
            )
        }
    }

    // MARK: - UserDefaults Operations

    private let userDefaults = UserDefaults.standard

    func savePreference<T: Codable>(_ value: T, forKey key: String) throws {
        let encoder = JSONEncoder()
        let data = try encoder.encode(value)
        userDefaults.set(data, forKey: Constants.Storage.userDefaultsPrefix + key)
    }

    func loadPreference<T: Codable>(forKey key: String) throws -> T? {
        guard let data = userDefaults.data(forKey: Constants.Storage.userDefaultsPrefix + key) else {
            return nil
        }
        let decoder = JSONDecoder()
        return try decoder.decode(T.self, from: data)
    }

    func removePreference(forKey key: String) {
        userDefaults.removeObject(forKey: Constants.Storage.userDefaultsPrefix + key)
    }

    func clearAllPreferences() {
        if let bundleIdentifier = Bundle.main.bundleIdentifier {
            userDefaults.removePersistentDomain(forName: bundleIdentifier)
        }
    }
}
