package com.swoosh.microblog.data.repository

import android.content.Context
import android.net.Uri
import com.swoosh.microblog.data.AccountManager
import com.swoosh.microblog.data.api.ApiClient
import com.swoosh.microblog.data.api.GhostApiService
import com.swoosh.microblog.data.db.AppDatabase
import com.swoosh.microblog.data.db.LocalPostDao
import com.swoosh.microblog.data.model.*
import com.swoosh.microblog.data.model.GhostNewsletter
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.withContext
import com.swoosh.microblog.data.model.PostFilter
import com.swoosh.microblog.data.model.SortOrder
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MultipartBody
import okhttp3.RequestBody.Companion.asRequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import java.io.File
import java.io.FileOutputStream

class PostRepository(private val context: Context) {

    private val accountManager = AccountManager(context)
    private val dao: LocalPostDao = AppDatabase.getInstance(context).localPostDao()

    fun getActiveAccountId(): String {
        return accountManager.getActiveAccount()?.id ?: ""
    }

    private fun getApi(): GhostApiService {
        val account = accountManager.getActiveAccount()
            ?: throw IllegalStateException("No active account configured")
        return ApiClient.getService(account.blogUrl) { account.apiKey }
    }

    // --- Remote operations ---

    suspend fun fetchPosts(
        page: Int = 1,
        limit: Int = 15,
        filter: PostFilter = PostFilter.ALL,
        sortOrder: SortOrder = SortOrder.NEWEST,
        tagFilter: String? = null
    ): Result<PostsResponse> =
        withContext(Dispatchers.IO) {
            try {
                // Combine status filter and tag filter
                val filterParts = mutableListOf<String>()
                filter.ghostFilter?.let { filterParts.add(it) }
                tagFilter?.let { filterParts.add("tag:$it") }
                val combinedFilter = filterParts.takeIf { it.isNotEmpty() }?.joinToString("+")

                val response = getApi().getPosts(
                    limit = limit,
                    page = page,
                    order = sortOrder.ghostOrder,
                    filter = combinedFilter
                )
                if (response.isSuccessful) {
                    Result.success(response.body()!!)
                } else {
                    Result.failure(Exception("API error ${response.code()}: ${response.errorBody()?.string()}"))
                }
            } catch (e: Exception) {
                Result.failure(e)
            }
        }

    suspend fun createPost(
        post: GhostPost,
        newsletter: String? = null,
        emailSegment: String? = null
    ): Result<GhostPost> =
        withContext(Dispatchers.IO) {
            try {
                val response = getApi().createPost(
                    PostWrapper(listOf(post)),
                    newsletter = newsletter,
                    emailSegment = emailSegment
                )
                if (response.isSuccessful) {
                    Result.success(response.body()!!.posts.first())
                } else {
                    Result.failure(Exception("Create failed ${response.code()}: ${response.errorBody()?.string()}"))
                }
            } catch (e: Exception) {
                Result.failure(e)
            }
        }

    suspend fun updatePost(
        id: String,
        post: GhostPost,
        newsletter: String? = null,
        emailSegment: String? = null
    ): Result<GhostPost> =
        withContext(Dispatchers.IO) {
            try {
                val response = getApi().updatePost(
                    id,
                    PostWrapper(listOf(post)),
                    newsletter = newsletter,
                    emailSegment = emailSegment
                )
                if (response.isSuccessful) {
                    Result.success(response.body()!!.posts.first())
                } else {
                    Result.failure(Exception("Update failed ${response.code()}: ${response.errorBody()?.string()}"))
                }
            } catch (e: Exception) {
                Result.failure(e)
            }
        }

    suspend fun fetchNewsletters(): Result<List<GhostNewsletter>> =
        withContext(Dispatchers.IO) {
            try {
                val response = getApi().getNewsletters()
                if (response.isSuccessful) {
                    Result.success(response.body()!!.newsletters)
                } else {
                    Result.failure(Exception("Newsletters fetch failed ${response.code()}: ${response.errorBody()?.string()}"))
                }
            } catch (e: Exception) {
                Result.failure(e)
            }
        }

    suspend fun fetchSubscriberCount(): Result<Int> =
        withContext(Dispatchers.IO) {
            try {
                val response = getApi().getMembers(limit = 1)
                if (response.isSuccessful) {
                    val total = response.body()!!.meta?.pagination?.total ?: 0
                    Result.success(total)
                } else {
                    Result.failure(Exception("Member count fetch failed ${response.code()}"))
                }
            } catch (e: Exception) {
                Result.failure(e)
            }
        }

    suspend fun deletePost(id: String): Result<Unit> =
        withContext(Dispatchers.IO) {
            try {
                val response = getApi().deletePost(id)
                if (response.isSuccessful) {
                    Result.success(Unit)
                } else {
                    Result.failure(Exception("Delete failed ${response.code()}"))
                }
            } catch (e: Exception) {
                Result.failure(e)
            }
        }

    suspend fun uploadImage(uri: Uri): Result<String> =
        withContext(Dispatchers.IO) {
            try {
                val mimeType = context.contentResolver.getType(uri) ?: "image/jpeg"
                val file = copyUriToTempFile(uri, ".jpg")
                val requestBody = file.asRequestBody(mimeType.toMediaType())
                val part = MultipartBody.Part.createFormData("file", file.name, requestBody)
                val purpose = "image".toRequestBody("text/plain".toMediaType())

                val response = getApi().uploadImage(part, purpose)
                file.delete()

                if (response.isSuccessful) {
                    val url = response.body()!!.images.first().url
                    Result.success(url)
                } else {
                    Result.failure(Exception("Upload failed ${response.code()}"))
                }
            } catch (e: Exception) {
                Result.failure(e)
            }
        }

    suspend fun uploadFile(uri: Uri): Result<String> =
        withContext(Dispatchers.IO) {
            try {
                val file = copyUriToTempFile(uri)
                val mimeType = context.contentResolver.getType(uri) ?: "application/octet-stream"
                val requestBody = file.asRequestBody(mimeType.toMediaType())
                val part = MultipartBody.Part.createFormData("file", file.name, requestBody)

                val response = getApi().uploadFile(part)
                file.delete()

                if (response.isSuccessful) {
                    val url = response.body()!!.files.first().url
                    Result.success(url)
                } else {
                    Result.failure(Exception("File upload failed ${response.code()}"))
                }
            } catch (e: Exception) {
                Result.failure(e)
            }
        }

    /**
     * Uploads multiple images and returns all uploaded URLs.
     * If any upload fails, returns failure with the error.
     */
    suspend fun uploadImages(uris: List<Uri>): Result<List<String>> =
        withContext(Dispatchers.IO) {
            try {
                val urls = mutableListOf<String>()
                for (uri in uris) {
                    val result = uploadImage(uri)
                    if (result.isFailure) {
                        return@withContext Result.failure(
                            result.exceptionOrNull() ?: Exception("Image upload failed")
                        )
                    }
                    urls.add(result.getOrThrow())
                }
                Result.success(urls)
            } catch (e: Exception) {
                Result.failure(e)
            }
        }

    /**
     * Uploads a media file (video or audio) to the Ghost media upload endpoint.
     * Determines MIME type from the content resolver. Returns the uploaded URL on success.
     */
    suspend fun uploadMediaFile(uri: Uri): Result<String> =
        withContext(Dispatchers.IO) {
            try {
                val mimeType = context.contentResolver.getType(uri) ?: "application/octet-stream"
                val extension = when {
                    mimeType.startsWith("video/") -> mimeType.substringAfter("video/").let { ".$it" }
                    mimeType.startsWith("audio/") -> mimeType.substringAfter("audio/").let { ".$it" }
                    else -> ""
                }
                val file = copyUriToTempFile(uri, extension)
                val requestBody = file.asRequestBody(mimeType.toMediaType())
                val part = MultipartBody.Part.createFormData("file", file.name, requestBody)

                val response = getApi().uploadMedia(part)
                file.delete()

                if (response.isSuccessful) {
                    val url = response.body()!!.media.first().url
                    Result.success(url)
                } else {
                    Result.failure(Exception("Media upload failed ${response.code()}"))
                }
            } catch (e: Exception) {
                Result.failure(e)
            }
        }

    private fun copyUriToTempFile(uri: Uri, extension: String = ".jpg"): File {
        val inputStream = context.contentResolver.openInputStream(uri)
            ?: throw IllegalStateException("Cannot open URI")
        val tempFile = File.createTempFile("upload_", extension, context.cacheDir)
        FileOutputStream(tempFile).use { output ->
            inputStream.copyTo(output)
        }
        inputStream.close()
        return tempFile
    }

    // --- Search operations ---

    suspend fun searchLocalPosts(query: String): List<LocalPost> =
        withContext(Dispatchers.IO) {
            dao.searchPosts(query)
        }

    suspend fun searchRemotePosts(query: String): Result<List<GhostPost>> =
        withContext(Dispatchers.IO) {
            try {
                // Ghost Admin API supports plaintext filter
                val response = getApi().getPosts(
                    limit = 50,
                    filter = "plaintext:~'$query'"
                )
                if (response.isSuccessful) {
                    Result.success(response.body()!!.posts)
                } else {
                    // Fallback: fetch posts and filter client-side
                    searchRemoteClientSide(query)
                }
            } catch (e: Exception) {
                // Fallback: fetch posts and filter client-side
                try {
                    searchRemoteClientSide(query)
                } catch (e2: Exception) {
                    Result.failure(e2)
                }
            }
        }

    private suspend fun searchRemoteClientSide(query: String): Result<List<GhostPost>> {
        val allPosts = mutableListOf<GhostPost>()
        var page = 1
        var hasMore = true

        while (hasMore && page <= 5) { // Limit to 5 pages for search
            val response = getApi().getPosts(limit = 50, page = page)
            if (response.isSuccessful) {
                val body = response.body()!!
                allPosts.addAll(body.posts)
                hasMore = body.meta?.pagination?.next != null
                page++
            } else {
                return Result.failure(Exception("API error ${response.code()}"))
            }
        }

        val queryLower = query.lowercase()
        val filtered = allPosts.filter { post ->
            (post.title?.lowercase()?.contains(queryLower) == true) ||
            (post.plaintext?.lowercase()?.contains(queryLower) == true) ||
            (post.html?.lowercase()?.contains(queryLower) == true)
        }
        return Result.success(filtered)
    }

    suspend fun searchPosts(query: String): Result<List<Pair<List<LocalPost>, List<GhostPost>>>> =
        withContext(Dispatchers.IO) {
            val localResults = searchLocalPosts(query)
            val remoteResult = try {
                searchRemotePosts(query)
            } catch (e: Exception) {
                Result.failure(e)
            }

            val remotePosts = remoteResult.getOrDefault(emptyList())
            Result.success(listOf(localResults to remotePosts))
        }

    // --- Local operations ---

    fun getLocalPosts(): Flow<List<LocalPost>> {
        val accountId = getActiveAccountId()
        return if (accountId.isNotEmpty()) {
            dao.getPostsByAccount(accountId)
        } else {
            dao.getAllPosts()
        }
    }

    fun getLocalPosts(filter: PostFilter, sortOrder: SortOrder): Flow<List<LocalPost>> {
        val status = filter.toPostStatus()
        return when {
            status != null && sortOrder == SortOrder.OLDEST -> dao.getPostsByStatusOldestFirst(status)
            status != null -> dao.getPostsByStatus(status)
            sortOrder == SortOrder.OLDEST -> dao.getAllPostsOldestFirst()
            else -> dao.getAllPosts() // NEWEST and RECENTLY_UPDATED both use updatedAt DESC
        }
    }

    suspend fun getQueuedPosts(): List<LocalPost> = dao.getQueuedPosts()

    suspend fun saveLocalPost(post: LocalPost): Long {
        val postWithAccount = if (post.accountId.isEmpty()) {
            post.copy(accountId = getActiveAccountId())
        } else {
            post
        }
        return dao.insertPost(postWithAccount)
    }

    suspend fun updateLocalPost(post: LocalPost) = dao.updatePost(post)

    suspend fun deleteLocalPost(localId: Long) = dao.deleteById(localId)

    suspend fun getLocalPostById(localId: Long): LocalPost? = dao.getPostById(localId)

    suspend fun getLocalPostByGhostId(ghostId: String): LocalPost? = dao.getPostByGhostId(ghostId)

    suspend fun updateQueueStatus(localId: Long, status: QueueStatus) =
        dao.updateQueueStatus(localId, status)

    suspend fun markUploaded(localId: Long, ghostId: String) =
        dao.markUploaded(localId, ghostId)

    // --- Stats queries ---

    suspend fun getTotalLocalPostCount(): Int = dao.getTotalPostCount()

    suspend fun getLocalPostCountByStatus(status: PostStatus): Int = dao.getPostCountByStatus(status)

    suspend fun getAllLocalPostsList(): List<LocalPost> = dao.getAllPostsList()

    // --- Featured/Pinned operations ---

    suspend fun updateLocalFeatured(localId: Long, featured: Boolean) =
        dao.updateFeatured(localId, featured)

    suspend fun updateLocalFeaturedByGhostId(ghostId: String, featured: Boolean) =
        dao.updateFeaturedByGhostId(ghostId, featured)

    suspend fun toggleFeatured(ghostId: String, featured: Boolean, updatedAt: String): Result<GhostPost> =
        withContext(Dispatchers.IO) {
            try {
                val post = GhostPost(
                    featured = featured,
                    updated_at = updatedAt
                )
                val response = getApi().updatePost(ghostId, PostWrapper(listOf(post)))
                if (response.isSuccessful) {
                    Result.success(response.body()!!.posts.first())
                } else {
                    Result.failure(Exception("Toggle featured failed ${response.code()}: ${response.errorBody()?.string()}"))
                }
            } catch (e: Exception) {
                Result.failure(e)
            }
        }

    // --- Connectivity check ---

    fun isNetworkAvailable(): Boolean {
        val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as android.net.ConnectivityManager
        val network = cm.activeNetwork ?: return false
        val capabilities = cm.getNetworkCapabilities(network) ?: return false
        return capabilities.hasCapability(android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET)
    }
}
