import Foundation
import Security
import Models

enum KeychainService {
	private static let service = "de.felixfoertsch.MagnumOpus"

	static func saveCredentials(_ credentials: Credentials, for accountId: String) throws {
		let passwordData = Data(credentials.password.utf8)

		let deleteQuery: [String: Any] = [
			kSecClass as String: kSecClassGenericPassword,
			kSecAttrService as String: service,
			kSecAttrAccount as String: accountId,
		]
		SecItemDelete(deleteQuery as CFDictionary)

		let query: [String: Any] = [
			kSecClass as String: kSecClassGenericPassword,
			kSecAttrService as String: service,
			kSecAttrAccount as String: accountId,
			kSecAttrLabel as String: credentials.username,
			kSecValueData as String: passwordData,
			kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlock,
		]

		let status = SecItemAdd(query as CFDictionary, nil)
		guard status == errSecSuccess else {
			throw KeychainError.saveFailed(status)
		}
	}

	static func loadCredentials(for accountId: String) throws -> Credentials? {
		let query: [String: Any] = [
			kSecClass as String: kSecClassGenericPassword,
			kSecAttrService as String: service,
			kSecAttrAccount as String: accountId,
			kSecReturnData as String: true,
			kSecReturnAttributes as String: true,
			kSecMatchLimit as String: kSecMatchLimitOne,
		]

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

		guard status == errSecSuccess,
			  let attrs = result as? [String: Any],
			  let data = attrs[kSecValueData as String] as? Data,
			  let password = String(data: data, encoding: .utf8),
			  let username = attrs[kSecAttrLabel as String] as? String
		else {
			if status == errSecItemNotFound { return nil }
			throw KeychainError.loadFailed(status)
		}

		return Credentials(username: username, password: password)
	}

	static func deleteCredentials(for accountId: String) throws {
		let query: [String: Any] = [
			kSecClass as String: kSecClassGenericPassword,
			kSecAttrService as String: service,
			kSecAttrAccount as String: accountId,
		]
		let status = SecItemDelete(query as CFDictionary)
		guard status == errSecSuccess || status == errSecItemNotFound else {
			throw KeychainError.deleteFailed(status)
		}
	}
}

enum KeychainError: Error {
	case saveFailed(OSStatus)
	case loadFailed(OSStatus)
	case deleteFailed(OSStatus)
}
