package ru.forbion.f7cloud.feature.files

import android.content.Context
import ru.forbion.f7cloud.core.auth.AuthSession
import ru.forbion.f7cloud.core.database.F7Database
import ru.forbion.f7cloud.core.database.FileEntity
import ru.forbion.f7cloud.core.network.DavClient
import ru.forbion.f7cloud.core.network.NetworkFactory
import ru.forbion.f7cloud.core.auth.OcsUserResolver
import ru.forbion.f7cloud.core.network.UnauthorizedException
import ru.forbion.f7cloud.core.network.davFileUrl
import ru.forbion.f7cloud.core.network.davFolderUrl
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody

class FilesRepository(context: Context) {
    private val db = F7Database.get(context)

    suspend fun listFolder(session: AuthSession, relativePath: String = ""): List<FileItem> {
        val client = NetworkFactory.newAuthedClient(
            session.username,
            session.appPassword,
            session.trustAllCerts,
        )
        val userId = session.davUserId ?: OcsUserResolver.resolveDavUserId(session)
        val folderUrl = davFolderUrl(session.serverUrl, userId, relativePath)
        val entries = DavClient.propfind(client, folderUrl)
        val items = entries.map {
            FileItem(
                name = it.name,
                isDirectory = it.isDirectory,
                relativePath = buildRelativePath(relativePath, it.name),
                fileId = it.fileId,
                lastModified = it.lastModified,
                size = it.size,
                mimeType = it.mimeType,
                favorite = it.favorite,
            )
        }
        if (relativePath.isBlank()) {
            val dao = db.filesDao()
            dao.clear(session.serverUrl, session.username)
            dao.insertAll(
                items.map {
                    FileEntity(
                        serverUrl = session.serverUrl,
                        username = session.username,
                        name = it.name,
                        isDirectory = it.isDirectory,
                    )
                },
            )
        }
        return items
    }

    suspend fun createFolder(session: AuthSession, relativeFolderPath: String, folderName: String) {
        ensureFolder(session, buildRelativePath(relativeFolderPath, folderName))
    }

    suspend fun ensureFolder(session: AuthSession, relativePath: String) {
        val client = NetworkFactory.newAuthedClient(
            session.username,
            session.appPassword,
            session.trustAllCerts,
        )
        val userId = session.davUserId ?: OcsUserResolver.resolveDavUserId(session)
        val folderUrl = davFolderUrl(session.serverUrl, userId, relativePath)
        runCatching { DavClient.mkcol(client, folderUrl) }
    }

    suspend fun uploadFile(
        session: AuthSession,
        relativeFolderPath: String,
        fileName: String,
        bytes: ByteArray,
        mimeType: String?,
    ) {
        val client = NetworkFactory.newAuthedClient(
            session.username,
            session.appPassword,
            session.trustAllCerts,
        )
        val userId = session.davUserId ?: OcsUserResolver.resolveDavUserId(session)
        val path = buildRelativePath(relativeFolderPath, fileName)
        val fileUrl = davFileUrl(session.serverUrl, userId, path)
        val mediaType = (mimeType?.takeIf { it.isNotBlank() } ?: "application/octet-stream")
            .toMediaType()
        DavClient.put(client, fileUrl, bytes.toRequestBody(mediaType))
    }

    suspend fun listCached(session: AuthSession): List<FileItem> {
        return db.filesDao()
            .list(session.serverUrl, session.username)
            .map {
                FileItem(
                    name = it.name,
                    isDirectory = it.isDirectory,
                    relativePath = it.name,
                )
            }
    }

    fun buildRelativePath(parent: String, name: String): String {
        val base = parent.trim('/')
        return if (base.isEmpty()) name else "$base/$name"
    }

    suspend fun deleteItem(session: AuthSession, relativePath: String, isDirectory: Boolean) {
        val client = NetworkFactory.newAuthedClient(
            session.username,
            session.appPassword,
            session.trustAllCerts,
        )
        val userId = session.davUserId ?: OcsUserResolver.resolveDavUserId(session)
        val url = if (isDirectory) {
            davFolderUrl(session.serverUrl, userId, relativePath)
        } else {
            davFileUrl(session.serverUrl, userId, relativePath)
        }
        DavClient.delete(client, url)
    }

    suspend fun renameItem(session: AuthSession, relativePath: String, newName: String, isDirectory: Boolean) {
        val client = NetworkFactory.newAuthedClient(
            session.username,
            session.appPassword,
            session.trustAllCerts,
        )
        val userId = session.davUserId ?: OcsUserResolver.resolveDavUserId(session)
        val parent = relativePath.substringBeforeLast('/', missingDelimiterValue = "")
        val destinationPath = buildRelativePath(parent, newName)
        val sourceUrl = if (isDirectory) {
            davFolderUrl(session.serverUrl, userId, relativePath)
        } else {
            davFileUrl(session.serverUrl, userId, relativePath)
        }
        val destinationUrl = if (isDirectory) {
            davFolderUrl(session.serverUrl, userId, destinationPath)
        } else {
            davFileUrl(session.serverUrl, userId, destinationPath)
        }
        DavClient.move(client, sourceUrl, destinationUrl)
    }

    suspend fun setFavorite(session: AuthSession, relativePath: String, favorite: Boolean, isDirectory: Boolean) {
        val client = NetworkFactory.newAuthedClient(
            session.username,
            session.appPassword,
            session.trustAllCerts,
        )
        val userId = session.davUserId ?: OcsUserResolver.resolveDavUserId(session)
        val url = if (isDirectory) {
            davFolderUrl(session.serverUrl, userId, relativePath)
        } else {
            davFileUrl(session.serverUrl, userId, relativePath)
        }
        DavClient.setFavorite(client, url, favorite)
    }

    fun uniqueName(existingNames: Collection<String>, baseName: String, extension: String): String {
        val ext = extension.takeIf { it.startsWith('.') } ?: ".$extension"
        val stem = baseName.removeSuffix(ext).ifBlank { baseName }
        val first = "$stem$ext"
        if (first !in existingNames) return first
        var index = 1
        while ("$stem ($index)$ext" in existingNames) index++
        return "$stem ($index)$ext"
    }
}
