package eu.darken.sdmse.common.files

import eu.darken.sdmse.common.sharedresource.HasSharedResource
import kotlinx.coroutines.flow.Flow
import okio.FileHandle
import java.time.Instant

interface APathGateway<
        P : APath,
        PLU : APathLookup<P>,
        PLUE : APathLookupExtended<P>,
        > : HasSharedResource<Any> {

    suspend fun createDir(path: P)

    suspend fun createFile(path: P)

    suspend fun listFiles(path: P): Collection<P>

    suspend fun lookup(path: P): PLU

    suspend fun lookupExtended(path: P): PLUE

    suspend fun lookupFiles(path: P): Collection<PLU>

    suspend fun lookupFilesExtended(path: P): Collection<PLUE>

    suspend fun walk(
        path: P,
        options: WalkOptions<P, PLU> = WalkOptions()
    ): Flow<PLU>

    data class WalkOptions<P : APath, PLU : APathLookup<P>>(
        val pathDoesNotContain: Set<String>? = null,
        val onFilter: (suspend (PLU) -> Boolean)? = null,
        val onError: (suspend (PLU, Exception) -> Boolean)? = null,
        val followSymlinks: Boolean = false,
    ) {
        // followSymlinks is intentionally NOT part of this check: symlink following is valid in
        // every mode. In ROOT/ADB it runs host-side via DirectLocalWalker (privileged process); in
        // AUTO, EscalatingWalker escalates symlinks it can't resolve app-side. Only onFilter/onError
        // force the app-side IndirectLocalWalker, since suspend callbacks can't cross the IPC boundary.
        val isDirect: Boolean
            get() = onFilter == null && onError == null
    }

    suspend fun du(
        path: P,
        options: DuOptions<P, PLU> = DuOptions()
    ): Long

    data class DuOptions<P : APath, PLU : APathLookup<P>>(
        val abortOnError: Boolean = false,
    )

    suspend fun exists(path: P): Boolean

    suspend fun canWrite(path: P): Boolean

    suspend fun canRead(path: P): Boolean

    suspend fun file(path: P, readWrite: Boolean): FileHandle

    suspend fun delete(path: P, recursive: Boolean)

    suspend fun createSymlink(linkPath: P, targetPath: P): Boolean

    suspend fun setModifiedAt(path: P, modifiedAt: Instant): Boolean

    suspend fun setPermissions(path: P, permissions: Permissions): Boolean

    suspend fun setOwnership(path: P, ownership: Ownership): Boolean
}