package wang.yaojia.webterm.di

import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import okhttp3.ConnectionPool
import okhttp3.OkHttpClient
import wang.yaojia.webterm.transport.ClientIdentity
import wang.yaojia.webterm.transport.ClientIdentityProvider
import wang.yaojia.webterm.transport.OkHttpClientFactory
import wang.yaojia.webterm.transport.OkHttpHttpTransport
import wang.yaojia.webterm.transport.OkHttpTermTransport
import wang.yaojia.webterm.tlsandroid.IdentityRepository
import wang.yaojia.webterm.wire.HttpTransport
import wang.yaojia.webterm.wire.TermTransport
import javax.inject.Singleton

/**
 * Network boundary (A15): the ONE shared [OkHttpClient] both transports use, the mTLS bridge, and the
 * WS + REST transports. One client → the `SSLSocketFactory` + `cache(null)` posture apply uniformly to
 * WS and REST, and the connection pool is shared (plan §2/§8).
 *
 * ### mTLS bridge (A11 [IdentityRepository] → A7 [ClientIdentityProvider])
 * `:transport-okhttp` never depends on `:client-tls-android`; this module is the bridge. The provider
 * reads the repository's stable `(SSLSocketFactory, X509TrustManager)` pair; the factory is backed by a
 * re-reading `X509KeyManager` so a mid-run cert rotation is presented on the NEXT handshake with no
 * client rebuild (R4). The provider is read ONCE, when the client is built.
 *
 * ### Breaking the construction cycle (A11's frozen constructor)
 * `AndroidIdentityRepository` needs the client for `connectionPool.evictAll()` on rotation, while the
 * client needs the repository's SSL material — a cycle. It is broken with an explicit shared
 * [ConnectionPool]: the repository is given a lightweight evict-only client that SHARES that pool
 * ([TlsModule]), and the shared client + its WS variant both use the same pool, so `evictAll()` drops
 * the real pooled/resumed connections. The graph is then acyclic (client → provider → repo → pool).
 */
@Module
@InstallIn(SingletonComponent::class)
public object NetworkModule {

    /** The one connection pool shared by the transports AND the repository's evict-only client. */
    @Provides
    @Singleton
    public fun provideConnectionPool(): ConnectionPool = ConnectionPool()

    /** Bridge the mTLS device identity onto the shared client (read once, at client build). */
    @Provides
    @Singleton
    public fun provideClientIdentityProvider(
        identityRepository: IdentityRepository,
    ): ClientIdentityProvider = ClientIdentityProvider {
        val material = identityRepository.sslMaterial()
        ClientIdentity(material.sslSocketFactory, material.trustManager)
    }

    /** The single shared client (plan §2 "one OkHttpClient"), pinned to the shared [ConnectionPool]. */
    @Provides
    @Singleton
    public fun provideOkHttpClient(
        identityProvider: ClientIdentityProvider,
        connectionPool: ConnectionPool,
    ): OkHttpClient =
        OkHttpClientFactory.create(identityProvider)
            .newBuilder()
            .connectionPool(connectionPool)
            .build()

    /** WS transport over the shared client (it derives a streaming-tuned variant sharing the pool). */
    @Provides
    @Singleton
    public fun provideTermTransport(client: OkHttpClient): TermTransport = OkHttpTermTransport(client)

    /** REST transport over the SAME shared client (Origin stamped by `:api-client`, never here). */
    @Provides
    @Singleton
    public fun provideHttpTransport(client: OkHttpClient): HttpTransport = OkHttpHttpTransport(client)
}
