package com.jishisongfu.duobibi.di

import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import com.jishisongfu.duobibi.BuildConfig
import com.jishisongfu.duobibi.data.api.ArenaApi
import com.jishisongfu.duobibi.data.api.AuthApi
import com.jishisongfu.duobibi.data.api.OcrApi
import com.jishisongfu.duobibi.data.api.ParseApi
import com.jishisongfu.duobibi.data.api.ProtectApi
import com.jishisongfu.duobibi.data.api.WorthBuyApi
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import kotlinx.serialization.json.Json
import okhttp3.Dns
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import java.net.Inet4Address
import java.net.InetAddress
import java.util.concurrent.TimeUnit
import javax.inject.Singleton

/**
 * 强制只走 IPv4 出站。
 *
 * 背景:debug 联调时 baseUrl=10.0.2.2,Android socket 默认双栈,会优先把
 * IPv4 地址 wrap 成 ::ffff:10.0.2.2 走 IPv6 出站。但模拟器 (尤其新版 AVD)
 * 的 IPv6 路由常不通,直接报 ENETUNREACH (Network is unreachable),
 * 表现就是 App 装好后随机一两次能连上,force-stop 重启就全挂。
 * 过滤掉 IPv6 让 OkHttp 只用 IPv4,模拟器/真机都稳。
 *
 * 注:必须放在 NetworkModule object **外面** — Hilt KSP 会 introspect module
 * 的每个 field 类型,Dns lambda 类型推断会让 KSP 报 NonExistentClass。
 */
private val ipv4OnlyDns: Dns = object : Dns {
    override fun lookup(hostname: String): List<InetAddress> {
        val all = Dns.SYSTEM.lookup(hostname)
        return all.filter { it is Inet4Address }.ifEmpty { all }
    }
}

@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {

    @Provides
    @Singleton
    fun provideJson(): Json = Json {
        ignoreUnknownKeys = true
        encodeDefaults = true
    }

    @Provides
    @Singleton
    fun provideOkHttp(): OkHttpClient = OkHttpClient.Builder()
        .connectTimeout(3, TimeUnit.SECONDS)
        .readTimeout(10, TimeUnit.SECONDS)
        .writeTimeout(10, TimeUnit.SECONDS)
        .dns(ipv4OnlyDns)
        .build()

    @Provides
    @Singleton
    fun provideRetrofit(json: Json, client: OkHttpClient): Retrofit = Retrofit.Builder()
        .baseUrl(BuildConfig.BASE_URL)
        .client(client)
        .addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
        .build()

    @Provides
    @Singleton
    fun provideParseApi(retrofit: Retrofit): ParseApi = retrofit.create(ParseApi::class.java)

    @Provides
    @Singleton
    fun provideAuthApi(retrofit: Retrofit): AuthApi = retrofit.create(AuthApi::class.java)

    /**
     * LLM 类接口(OCR / 比价擂台 / 值不值得买 / 价保查价)端到端可能 5-15s,
     * 远超默认 readTimeout=10s,统一走这套长超时 Retrofit。
     */
    @Provides
    @Singleton
    @javax.inject.Named("llm")
    fun provideLlmOkHttp(): OkHttpClient = OkHttpClient.Builder()
        .connectTimeout(5, TimeUnit.SECONDS)
        .readTimeout(45, TimeUnit.SECONDS)
        .writeTimeout(45, TimeUnit.SECONDS)
        .dns(ipv4OnlyDns)
        .build()

    @Provides
    @Singleton
    @javax.inject.Named("llm")
    fun provideLlmRetrofit(
        json: Json,
        @javax.inject.Named("llm") client: OkHttpClient,
    ): Retrofit = Retrofit.Builder()
        .baseUrl(BuildConfig.BASE_URL)
        .client(client)
        .addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
        .build()

    @Provides
    @Singleton
    fun provideOcrApi(@javax.inject.Named("llm") retrofit: Retrofit): OcrApi =
        retrofit.create(OcrApi::class.java)

    @Provides
    @Singleton
    fun provideProtectApi(@javax.inject.Named("llm") retrofit: Retrofit): ProtectApi =
        retrofit.create(ProtectApi::class.java)

    @Provides
    @Singleton
    fun provideArenaApi(@javax.inject.Named("llm") retrofit: Retrofit): ArenaApi =
        retrofit.create(ArenaApi::class.java)

    @Provides
    @Singleton
    fun provideWorthBuyApi(@javax.inject.Named("llm") retrofit: Retrofit): WorthBuyApi =
        retrofit.create(WorthBuyApi::class.java)
}
