import Dependencies
import Foundation
import SimpleKeychain

public extension DependencyValues {
    var keychainClient: KeychainClient {
        get { self[KeychainClient.self] }
        set { self[KeychainClient.self] = newValue }
    }
}

public struct KeychainClient {
    public var getString: @Sendable (String) throws -> String?

    public var setString: @Sendable (_ value: String, _ key: String) throws -> Void

    public var delete: @Sendable (String) throws -> Void

    public var hasItem: @Sendable (String) throws -> Bool
}

// MARK: - DependencyKey

extension KeychainClient: DependencyKey {
    private enum Constants {
        static let keychainService = Bundle.main.infoDictionary?["KEYCHAIN_SERVICE"] as? String ?? "ai.suno.ios.KeychainClient.service"
    }

    public static let liveValue: Self = {
        let keychain = SimpleKeychain(service: Constants.keychainService)

        return Self(
            getString: { key in
                try keychain.string(forKey: key)
            },
            setString: { value, key in
                try keychain.set(value, forKey: key)
            },
            delete: { key in
                try keychain.deleteItem(forKey: key)
            },
            hasItem: { key in
                try keychain.hasItem(forKey: key)
            }
        )
    }()
}

// MARK: - TestDependencyKey

extension KeychainClient: TestDependencyKey {
    public static let testValue: Self = Self.isolatedTestValue()

    public static func isolatedTestValue() -> Self {
        let storage = LockIsolated<[String: String]>([:])

        return Self(
            getString: { key in
                storage.value[key]
            },
            setString: { value, key in
                storage.withValue { $0[key] = value }
            },
            delete: { key in
                storage.withValue { _ = $0.removeValue(forKey: key) }
            },
            hasItem: { key in
                storage.value[key] != nil
            }
        )
    }

    public static let failingValue = Self(
        getString: { _ in throw NSError.keychainAccessFailed(localizedDescription: "Keychain getter failed") },
        setString: { _, _ in throw NSError.keychainAccessFailed(localizedDescription: "Keychain setter failed") },
        delete: { _ in throw NSError.keychainAccessFailed(localizedDescription: "Keychain delete failed") },
        hasItem: { _ in throw NSError.keychainAccessFailed(localizedDescription: "Keychain hasItem failed") }
    )
}

// MARK: - NSError+KeychainError

private extension NSError {
    static func keychainAccessFailed(
        localizedDescription: String
    ) -> NSError {
        NSError(
            domain: "KeychainClient",
            code: -1,
            userInfo: [NSLocalizedDescriptionKey: localizedDescription]
        )
    }
}
