import Foundation
import Security
import LocalAuthentication

enum KeychainService {
    private static let service = "com.encryptedchat.credentials"
    private static let account = "userCredentials"

    struct Credentials: Codable {
        let email: String
        let password: String
        let host: String
        let port: UInt16
    }

    /// Check if saved credentials exist without triggering biometric prompt.
    static func hasSavedCredentials() -> Bool {
        let context = LAContext()
        context.interactionNotAllowed = true

        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrService as String: service,
            kSecAttrAccount as String: account,
            kSecMatchLimit as String: kSecMatchLimitOne,
            kSecReturnAttributes as String: true,
            kSecUseAuthenticationContext as String: context
        ]
        var result: AnyObject?
        let status = SecItemCopyMatching(query as CFDictionary, &result)
        // errSecInteractionNotAllowed means item exists but needs biometric
        return status == errSecSuccess || status == errSecInteractionNotAllowed
    }

    /// Save credentials to Keychain with biometric protection.
    static func saveCredentials(email: String, password: String, host: String, port: UInt16) throws {
        // Delete any existing entry first
        deleteCredentials()

        let credentials = Credentials(email: email, password: password, host: host, port: port)
        let data = try JSONEncoder().encode(credentials)

        var accessError: Unmanaged<CFError>?
        guard let accessControl = SecAccessControlCreateWithFlags(
            kCFAllocatorDefault,
            kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
            .biometryAny,
            &accessError
        ) else {
            throw KeychainError.accessControlCreationFailed
        }

        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrService as String: service,
            kSecAttrAccount as String: account,
            kSecValueData as String: data,
            kSecAttrAccessControl as String: accessControl
        ]

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

    /// Load credentials from Keychain. Triggers biometric prompt.
    static func loadCredentials() throws -> Credentials {
        let context = LAContext()
        context.localizedReason = "Unlock to log in"

        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrService as String: service,
            kSecAttrAccount as String: account,
            kSecReturnData as String: true,
            kSecMatchLimit as String: kSecMatchLimitOne,
            kSecUseAuthenticationContext as String: context
        ]

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

        guard status == errSecSuccess, let data = result as? Data else {
            if status == errSecUserCanceled || status == errSecAuthFailed {
                throw KeychainError.biometricFailed
            }
            throw KeychainError.loadFailed(status)
        }

        return try JSONDecoder().decode(Credentials.self, from: data)
    }

    /// Delete stored credentials from Keychain.
    @discardableResult
    static func deleteCredentials() -> Bool {
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrService as String: service,
            kSecAttrAccount as String: account
        ]
        let status = SecItemDelete(query as CFDictionary)
        return status == errSecSuccess || status == errSecItemNotFound
    }

    /// Check if biometric authentication is available on this device.
    static func isBiometricAvailable() -> Bool {
        let context = LAContext()
        var error: NSError?
        return context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error)
    }

    enum KeychainError: LocalizedError {
        case accessControlCreationFailed
        case saveFailed(OSStatus)
        case loadFailed(OSStatus)
        case biometricFailed

        var errorDescription: String? {
            switch self {
            case .accessControlCreationFailed:
                return "Failed to create biometric access control"
            case .saveFailed(let status):
                return "Failed to save credentials (error \(status))"
            case .loadFailed(let status):
                return "Failed to load credentials (error \(status))"
            case .biometricFailed:
                return "Biometric authentication failed or was cancelled"
            }
        }
    }
}
