import Apollo
import ApolloSQLite
import Foundation

final class Network {
    static let backendURL = URL(string: "<https://example.com>")!

    static let shared = Network()

    // MARK: Apollo

    private lazy var networkTransport: NetworkTransport = {
        let client = URLSessionClient()
        let provider = Provider(
            client: client,
            shouldInvalidateClientOnDeinit: true,
            store: store ?? fallbackStore
        )

        return RequestChainNetworkTransport(
            interceptorProvider: provider,
            endpointURL: Network.backendURL.appendingPathComponent("graphql", isDirectory: true)
        )
    }()

    private lazy var fallbackStore = ApolloStore(cache: InMemoryNormalizedCache())

    private lazy var store: ApolloStore? = {
        guard let sqliteUrl = FileManager()
            .urls(for: .documentDirectory, in: .userDomainMask)
            .first?.appendingPathComponent("project_name.sqlite") else { return nil }
        guard let sqliteCache = try? SQLiteNormalizedCache(fileURL: sqliteUrl) else { return nil }
        return ApolloStore(cache: sqliteCache)
    }()

    private(set) lazy var apollo: ApolloClient = {
        let apollo = ApolloClient(networkTransport: networkTransport, store: store ?? fallbackStore)
        apollo.cacheKeyForObject = { $0["id"] }
        return apollo
    }()
}

private class Provider: LegacyInterceptorProvider {
    override func interceptors<Operation: GraphQLOperation>(for operation: Operation) -> [ApolloInterceptor] {
        var interceptors = super.interceptors(for: operation)
        interceptors.insert(TokenAddingInterceptor(), at: 0)
        return interceptors
    }
}

private class TokenAddingInterceptor: ApolloInterceptor {
    func interceptAsync<Operation: GraphQLOperation>(
        chain: RequestChain,
        request: HTTPRequest<Operation>,
        response: HTTPResponse<Operation>?,
        completion: @escaping (Result<GraphQLResult<Operation.Data>, Error>) -> Void
    ) {
        if let token = UserDefaults.authToken {
            request.addHeader(name: "Authorization", value: "JWT \\(token)")
        }
        request.addHeader(name: "Accept-Language", value: NSLocale.preferredLanguages.joined(separator: ", "))
        chain.proceedAsync(request: request, response: response, completion: completion)
    }
}