// SPDX-License-Identifier: LicenseRef-Hive-Proprietary OR GPL-3.0-only

import Foundation
import Security

enum SecureSessionStoreError: LocalizedError {
    case keychain(OSStatus)
    case invalidData

    var errorDescription: String? {
        switch self {
        case .keychain(let status):
            let detail = SecCopyErrorMessageString(status, nil) as String?
                ?? L10n.text("error.keychain_unknown", fallback: "unknown error")
            return L10n.text(
                "error.keychain",
                fallback: "Could not access VPN credentials: %@ (%d)",
                detail,
                Int(status)
            )
        case .invalidData:
            return L10n.text(
                "error.invalid_saved_credentials",
                fallback: "The saved VPN credentials are invalid"
            )
        }
    }
}

struct SecureSessionStore {
    private let service = "net.hivetechnology.o20010.06.mobile-session"
    private let account = "active"

    var installationId: String {
        let defaults = UserDefaults.standard
        if let current = defaults.string(forKey: "installation-id") { return current }
        let value = UUID().uuidString.lowercased()
        defaults.set(value, forKey: "installation-id")
        return value
    }

    func save(_ credentials: SessionCredentials) throws {
        let data = try JSONEncoder().encode(credentials)
        var query = baseQuery
        SecItemDelete(query as CFDictionary)
        query[kSecValueData as String] = data
        query[kSecAttrAccessible as String] = kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
        let status = SecItemAdd(query as CFDictionary, nil)
        guard status == errSecSuccess else { throw SecureSessionStoreError.keychain(status) }
    }

    func load() throws -> SessionCredentials? {
        var query = baseQuery
        query[kSecReturnData as String] = true
        query[kSecMatchLimit as String] = kSecMatchLimitOne
        var item: CFTypeRef?
        let status = SecItemCopyMatching(query as CFDictionary, &item)
        if status == errSecItemNotFound { return nil }
        guard status == errSecSuccess else { throw SecureSessionStoreError.keychain(status) }
        guard let data = item as? Data,
              let credentials = try? JSONDecoder().decode(SessionCredentials.self, from: data) else {
            throw SecureSessionStoreError.invalidData
        }
        return credentials
    }

    func clear() {
        SecItemDelete(baseQuery as CFDictionary)
    }

    private var baseQuery: [String: Any] {
        var query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrService as String: service,
            kSecAttrAccount as String: account,
        ]
        if let accessGroup = Bundle.main.object(forInfoDictionaryKey: "HTKeychainAccessGroup") as? String,
           !accessGroup.isEmpty {
            query[kSecAttrAccessGroup as String] = accessGroup
        }
        return query
    }
}
