package ru.forbion.f7cloud.core.network

import okhttp3.Cookie
import okhttp3.CookieJar
import okhttp3.FormBody
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import java.util.concurrent.ConcurrentHashMap

/**
 * Устанавливает НАСТОЯЩУЮ веб-сессию Nextcloud (cookie + CSRF) по форме `/login`, используя
 * app-пароль как пароль (он валиден для входа и обходит 2FA). Нужно для встраивания веб-viewer'ов
 * (доска Whiteboard и т.п.), которым мало Basic-auth: их Vue-XHR ходят по сессии.
 */
object NextcloudWebSession {
    private val requestTokenRegex = Regex(
        """(?:name=["']requesttoken["']\s+value|<meta\s+name=["']requesttoken["']\s+content)=["']([^"']+)["']""",
        RegexOption.IGNORE_CASE,
    )

    /** Cookie, собранные при логине (для переноса в WebView CookieManager). */
    fun login(
        serverUrl: String,
        username: String,
        appPassword: String,
        trustAllCerts: Boolean = false,
    ): List<Cookie> {
        val base = serverUrl.trimEnd('/')
        val host = base.toHttpUrl().host
        val jar = MemoryCookieJar()
        val client = NetworkFactory.newBaseBuilder(trustAllCerts)
            .cookieJar(jar)
            .followRedirects(true)
            .followSslRedirects(true)
            .build()

        // 1. GET страницы логина — начальные cookie + requesttoken.
        val loginHtml = client.newCall(
            Request.Builder().url("$base/login").header("User-Agent", USER_AGENT).get().build(),
        ).execute().use { it.body?.string().orEmpty() }
        val token = requestTokenRegex.find(loginHtml)?.groupValues?.getOrNull(1).orEmpty()

        // 2. POST формы логина app-паролем — сервер ставит session-cookie.
        val form = FormBody.Builder()
            .add("user", username)
            .add("password", appPassword)
            .apply { if (token.isNotBlank()) add("requesttoken", token) }
            .add("remember_login", "1")
            .add("timezone", "UTC")
            .add("timezone_offset", "0")
            .build()
        client.newCall(
            Request.Builder()
                .url("$base/login")
                .header("User-Agent", USER_AGENT)
                .apply { if (token.isNotBlank()) header("requesttoken", token) }
                .header("OCS-APIRequest", "true")
                .post(form)
                .build(),
        ).execute().use { /* cookie осели в jar */ }

        return jar.cookiesForHost(host)
    }

    private const val USER_AGENT = "Mozilla/5.0 (Android) F7cloud-Mobile"

    private class MemoryCookieJar : CookieJar {
        private val store = ConcurrentHashMap<String, MutableList<Cookie>>()
        override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
            val list = store.getOrPut(url.host) { mutableListOf() }
            cookies.forEach { c ->
                list.removeAll { it.name == c.name }
                list += c
            }
        }
        override fun loadForRequest(url: HttpUrl): List<Cookie> =
            store[url.host].orEmpty().filter { it.matches(url) }
        fun cookiesForHost(host: String): List<Cookie> = store[host].orEmpty().toList()
    }
}
