package com.swoosh.microblog.ui.composer

import android.app.Application
import android.net.Uri
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import com.swoosh.microblog.data.HashtagParser
import com.swoosh.microblog.data.MobiledocBuilder
import com.swoosh.microblog.data.NewsletterPreferences
import com.swoosh.microblog.data.TagsPreferences
import com.swoosh.microblog.data.PreviewHtmlBuilder
import com.swoosh.microblog.data.db.Converters
import com.swoosh.microblog.data.model.*
import com.swoosh.microblog.data.repository.OpenGraphFetcher
import com.swoosh.microblog.data.repository.PostRepository
import com.swoosh.microblog.data.repository.TagRepository
import com.swoosh.microblog.worker.PostUploadWorker
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch

class ComposerViewModel(application: Application) : AndroidViewModel(application) {

    private val repository = PostRepository(application)
    private val tagRepository = TagRepository(application)
    private val newsletterPreferences = NewsletterPreferences(application)
    private val tagsPreferences = TagsPreferences(application)
    private val appContext = application

    private val _uiState = MutableStateFlow(ComposerUiState())
    val uiState: StateFlow<ComposerUiState> = _uiState.asStateFlow()

    private var editingLocalId: Long? = null
    private var editingGhostId: String? = null
    private var editingUpdatedAt: String? = null

    private var previewDebounceJob: Job? = null

    init {
        if (tagsPreferences.isTagsEnabled()) {
            loadAvailableTags()
        }
        loadNewsletterData()
    }

    fun isTagsEnabled(): Boolean = tagsPreferences.isTagsEnabled()

    private fun loadAvailableTags() {
        viewModelScope.launch {
            tagRepository.fetchTags().fold(
                onSuccess = { tags ->
                    _uiState.update { it.copy(availableTags = tags) }
                },
                onFailure = { /* silently ignore - tags are optional */ }
            )
        }
    }

    private fun loadNewsletterData() {
        val enabled = newsletterPreferences.isNewsletterEnabled()
        _uiState.update { it.copy(newsletterEnabled = enabled) }
        if (enabled) {
            viewModelScope.launch {
                // Fetch available newsletters
                repository.fetchNewsletters().fold(
                    onSuccess = { newsletters ->
                        _uiState.update { state ->
                            state.copy(
                                availableNewsletters = newsletters,
                                selectedNewsletter = newsletters.firstOrNull()
                            )
                        }
                    },
                    onFailure = { /* silently ignore */ }
                )
                // Fetch subscriber count (lightweight: limit=1, read meta.pagination.total)
                repository.fetchSubscriberCount().fold(
                    onSuccess = { count ->
                        _uiState.update { it.copy(subscriberCount = count) }
                    },
                    onFailure = { /* silently ignore */ }
                )
            }
        }
    }

    fun toggleSendAsNewsletter() {
        _uiState.update { it.copy(sendAsNewsletter = !it.sendAsNewsletter) }
    }

    fun selectNewsletter(newsletter: GhostNewsletter) {
        _uiState.update { it.copy(selectedNewsletter = newsletter) }
    }

    fun setEmailSegment(segment: String) {
        _uiState.update { it.copy(emailSegment = segment) }
    }

    fun confirmNewsletterSend() {
        _uiState.update { it.copy(showNewsletterConfirmation = false) }
        publish()
    }

    fun cancelNewsletterConfirmation() {
        _uiState.update { it.copy(showNewsletterConfirmation = false) }
    }

    fun sendEmailOnly() {
        _uiState.update { it.copy(showEmailOnlyConfirmation = true) }
    }

    fun confirmEmailOnly() {
        _uiState.update { it.copy(showEmailOnlyConfirmation = false) }
        submitEmailOnlyPost()
    }

    fun cancelEmailOnly() {
        _uiState.update { it.copy(showEmailOnlyConfirmation = false) }
    }

    private fun submitEmailOnlyPost() {
        val state = _uiState.value
        if (state.text.isBlank() && state.imageUris.isEmpty() && state.fileUri == null) return

        viewModelScope.launch {
            _uiState.update { it.copy(isSubmitting = true, error = null) }

            val title = state.text.take(60)
            val hashtagTags = HashtagParser.parse(state.text)
            val allTags = (state.extractedTags + hashtagTags).distinctBy { it.lowercase() }
            val tagsJson = Gson().toJson(allTags)
            val altText = state.imageAlt.ifBlank { null }
            val newsletterSlug = state.selectedNewsletter?.slug

            // Save locally and queue for upload
            val localPost = LocalPost(
                localId = editingLocalId ?: 0,
                ghostId = editingGhostId,
                title = title,
                content = state.text,
                status = PostStatus.PUBLISHED,
                featured = false,
                imageUri = state.imageUris.firstOrNull()?.toString(),
                imageUris = Converters.stringListToJson(state.imageUris.map { it.toString() }),
                imageAlt = altText,
                linkUrl = state.linkPreview?.url,
                linkTitle = state.linkPreview?.title,
                linkDescription = state.linkPreview?.description,
                linkImageUrl = state.linkPreview?.imageUrl,
                tags = tagsJson,
                queueStatus = QueueStatus.QUEUED_EMAIL_ONLY,
                emailOnly = true,
                newsletterSlug = newsletterSlug,
                fileUri = state.fileUri?.toString(),
                fileName = state.fileName
            )
            repository.saveLocalPost(localPost)
            PostUploadWorker.enqueue(appContext)

            _uiState.update { it.copy(isSubmitting = false, isSuccess = true) }
        }
    }

    fun updateTagInput(input: String) {
        val suggestions = if (input.isBlank()) {
            emptyList()
        } else {
            _uiState.value.availableTags.filter { tag ->
                tag.name.contains(input, ignoreCase = true) &&
                    !_uiState.value.extractedTags.any { it.equals(tag.name, ignoreCase = true) }
            }.take(5)
        }
        _uiState.update { it.copy(tagInput = input, tagSuggestions = suggestions) }
    }

    fun addTag(tagName: String) {
        val currentTags = _uiState.value.extractedTags.toMutableList()
        if (!currentTags.any { it.equals(tagName, ignoreCase = true) }) {
            currentTags.add(tagName)
        }
        _uiState.update { it.copy(extractedTags = currentTags, tagInput = "", tagSuggestions = emptyList()) }
    }

    fun removeTag(tagName: String) {
        val currentTags = _uiState.value.extractedTags.toMutableList()
        currentTags.removeAll { it.equals(tagName, ignoreCase = true) }
        _uiState.update { it.copy(extractedTags = currentTags) }
    }

    fun loadForEdit(post: FeedPost) {
        editingLocalId = post.localId
        editingGhostId = post.ghostId
        editingUpdatedAt = post.updatedAt

        // Build image URIs list from available data
        val imageUris = mutableListOf<Uri>()
        if (post.imageUrls.isNotEmpty()) {
            imageUris.addAll(post.imageUrls.map { Uri.parse(it) })
        } else if (post.imageUrl != null) {
            imageUris.add(Uri.parse(post.imageUrl))
        }

        _uiState.update {
            it.copy(
                text = post.textContent,
                imageUris = imageUris,
                imageAlt = post.imageAlt ?: "",
                videoUri = post.videoUrl?.let { url -> Uri.parse(url) },
                audioUri = post.audioUrl?.let { url -> Uri.parse(url) },
                linkPreview = if (post.linkUrl != null) LinkPreview(
                    url = post.linkUrl,
                    title = post.linkTitle,
                    description = post.linkDescription,
                    imageUrl = post.linkImageUrl
                ) else null,
                featured = post.featured,
                extractedTags = HashtagParser.parse(post.textContent),
                isEditing = true
            )
        }
    }

    fun updateText(text: String) {
        val extractedTags = HashtagParser.parse(text)
        _uiState.update { it.copy(text = text, extractedTags = extractedTags) }
        if (_uiState.value.isPreviewMode) {
            debouncedPreviewUpdate()
        }
    }

    /**
     * Legacy single image setter - adds to the list.
     */
    fun setImage(uri: Uri?) {
        if (uri != null) {
            addImages(listOf(uri))
        } else {
            _uiState.update { it.copy(imageUris = emptyList(), imageAlt = "") }
        }
        if (_uiState.value.isPreviewMode) {
            debouncedPreviewUpdate()
        }
    }

    fun updateImageAlt(alt: String) {
        _uiState.update { it.copy(imageAlt = alt.take(250)) }
    }

    /**
     * Adds multiple images to the current selection.
     */
    fun addImages(uris: List<Uri>) {
        _uiState.update { state ->
            state.copy(imageUris = state.imageUris + uris)
        }
    }

    /**
     * Removes a specific image by index.
     */
    fun removeImage(index: Int) {
        _uiState.update { state ->
            if (index in state.imageUris.indices) {
                state.copy(imageUris = state.imageUris.toMutableList().apply { removeAt(index) })
            } else state
        }
    }

    /**
     * Attach a file to the post. Reads filename and size from ContentResolver.
     * Validates type and size (max 50 MB).
     */
    fun addFile(uri: Uri) {
        val contentResolver = appContext.contentResolver
        var name: String? = null
        var size: Long? = null

        contentResolver.query(uri, null, null, null, null)?.use { cursor ->
            if (cursor.moveToFirst()) {
                val nameIndex = cursor.getColumnIndex(android.provider.OpenableColumns.DISPLAY_NAME)
                val sizeIndex = cursor.getColumnIndex(android.provider.OpenableColumns.SIZE)
                if (nameIndex >= 0) name = cursor.getString(nameIndex)
                if (sizeIndex >= 0) size = cursor.getLong(sizeIndex)
            }
        }

        val mimeType = contentResolver.getType(uri) ?: "application/octet-stream"

        // Validate size: max 50 MB
        val maxSize = 50L * 1024 * 1024
        if (size != null && size!! > maxSize) {
            _uiState.update { it.copy(error = "File too large. Maximum file size is 50 MB.") }
            return
        }

        _uiState.update {
            it.copy(
                fileUri = uri,
                fileName = name ?: "file",
                fileSize = size,
                fileMimeType = mimeType,
                uploadedFileUrl = null,
                error = null
            )
        }
    }

    fun removeFile() {
        _uiState.update {
            it.copy(
                fileUri = null,
                fileName = null,
                fileSize = null,
                fileMimeType = null,
                uploadedFileUrl = null
            )
        }
    }

    fun setVideo(uri: Uri) {
        _uiState.update { it.copy(videoUri = uri) }
    }

    fun removeVideo() {
        _uiState.update { it.copy(videoUri = null, uploadedVideoUrl = null) }
    }

    fun setAudio(uri: Uri) {
        _uiState.update { it.copy(audioUri = uri) }
    }

    fun removeAudio() {
        _uiState.update { it.copy(audioUri = null, uploadedAudioUrl = null) }
    }

    fun fetchLinkPreview(url: String) {
        if (url.isBlank()) return
        viewModelScope.launch {
            _uiState.update { it.copy(isLoadingLink = true) }
            val preview = OpenGraphFetcher.fetch(url)
            _uiState.update { it.copy(linkPreview = preview, isLoadingLink = false) }
            if (_uiState.value.isPreviewMode) {
                generatePreviewHtml()
            }
        }
    }

    fun removeLinkPreview() {
        _uiState.update { it.copy(linkPreview = null) }
        if (_uiState.value.isPreviewMode) {
            debouncedPreviewUpdate()
        }
    }

    fun setScheduledDate(dateTimeIso: String?) {
        _uiState.update { it.copy(scheduledAt = dateTimeIso) }
    }

    /**
     * Toggle between edit and preview modes.
     */
    fun togglePreviewMode() {
        val currentlyInPreview = _uiState.value.isPreviewMode
        if (!currentlyInPreview) {
            // Switching to preview: generate HTML immediately
            generatePreviewHtml()
        }
        _uiState.update { it.copy(isPreviewMode = !currentlyInPreview) }
    }

    /**
     * Switch to a specific mode directly.
     */
    fun setPreviewMode(preview: Boolean) {
        if (preview && !_uiState.value.isPreviewMode) {
            generatePreviewHtml()
        }
        _uiState.update { it.copy(isPreviewMode = preview) }
    }

    private fun debouncedPreviewUpdate() {
        previewDebounceJob?.cancel()
        previewDebounceJob = viewModelScope.launch {
            delay(PREVIEW_DEBOUNCE_MS)
            generatePreviewHtml()
        }
    }

    internal fun generatePreviewHtml() {
        val state = _uiState.value
        val html = PreviewHtmlBuilder.build(
            text = state.text,
            imageUrl = state.imageUri?.toString(),
            linkPreview = state.linkPreview
        )
        _uiState.update { it.copy(previewHtml = html) }
    }

    fun toggleFeatured() {
        _uiState.update { it.copy(featured = !it.featured) }
    }

    fun publish() {
        val state = _uiState.value
        if (state.sendAsNewsletter && state.selectedNewsletter != null && !state.showNewsletterConfirmation) {
            // Show confirmation dialog before sending as newsletter
            _uiState.update { it.copy(showNewsletterConfirmation = true) }
            return
        }
        submitPost(PostStatus.PUBLISHED, QueueStatus.QUEUED_PUBLISH)
    }

    fun saveDraft() = submitPost(PostStatus.DRAFT, QueueStatus.NONE)

    fun schedule() {
        val scheduledAt = _uiState.value.scheduledAt ?: return
        submitPost(PostStatus.SCHEDULED, QueueStatus.QUEUED_SCHEDULED)
    }

    private fun submitPost(status: PostStatus, offlineQueueStatus: QueueStatus) {
        val state = _uiState.value
        if (state.text.isBlank() && state.imageUris.isEmpty() && state.fileUri == null && state.videoUri == null && state.audioUri == null) return

        viewModelScope.launch {
            _uiState.update { it.copy(isSubmitting = true, error = null) }

            val title = state.text.take(60)
            // Merge hashtag-parsed tags with manually-added tags (deduplicated)
            val hashtagTags = HashtagParser.parse(state.text)
            val allTags = (state.extractedTags + hashtagTags).distinctBy { it.lowercase() }
            val tagsJson = Gson().toJson(allTags)

            val altText = state.imageAlt.ifBlank { null }

            if (status == PostStatus.DRAFT || !repository.isNetworkAvailable()) {
                // Save locally
                val localPost = LocalPost(
                    localId = editingLocalId ?: 0,
                    ghostId = editingGhostId,
                    title = title,
                    content = state.text,
                    status = status,
                    featured = if (status != PostStatus.DRAFT) state.featured else false,
                    imageUri = state.imageUris.firstOrNull()?.toString(),
                    imageUris = Converters.stringListToJson(state.imageUris.map { it.toString() }),
                    imageAlt = altText,
                    videoUri = state.videoUri?.toString(),
                    audioUri = state.audioUri?.toString(),
                    linkUrl = state.linkPreview?.url,
                    linkTitle = state.linkPreview?.title,
                    linkDescription = state.linkPreview?.description,
                    linkImageUrl = state.linkPreview?.imageUrl,
                    scheduledAt = state.scheduledAt,
                    tags = tagsJson,
                    queueStatus = if (status == PostStatus.DRAFT) QueueStatus.NONE else offlineQueueStatus,
                    fileUri = state.fileUri?.toString(),
                    fileName = state.fileName,
                    newsletterSlug = if (state.sendAsNewsletter) state.selectedNewsletter?.slug else null
                )
                repository.saveLocalPost(localPost)

                if (status != PostStatus.DRAFT) {
                    PostUploadWorker.enqueue(appContext)
                }

                _uiState.update { it.copy(isSubmitting = false, isSuccess = true) }
                return@launch
            }

            // Online: upload all images first
            val uploadedImageUrls = mutableListOf<String>()
            if (state.imageUris.isNotEmpty()) {
                val imagesResult = repository.uploadImages(state.imageUris)
                imagesResult.fold(
                    onSuccess = { urls -> uploadedImageUrls.addAll(urls) },
                    onFailure = { e ->
                        _uiState.update { it.copy(isSubmitting = false, error = "Image upload failed: ${e.message}") }
                        return@launch
                    }
                )
            }

            // Upload video if present
            var videoUrl: String? = state.uploadedVideoUrl
            if (videoUrl == null && state.videoUri != null) {
                _uiState.update { it.copy(isUploadingMedia = true) }
                val videoResult = repository.uploadMediaFile(state.videoUri)
                videoResult.fold(
                    onSuccess = { url -> videoUrl = url },
                    onFailure = { e ->
                        _uiState.update { it.copy(isSubmitting = false, isUploadingMedia = false, error = "Video upload failed: ${e.message}") }
                        return@launch
                    }
                )
            }

            // Upload audio if present
            var audioUrl: String? = state.uploadedAudioUrl
            if (audioUrl == null && state.audioUri != null) {
                _uiState.update { it.copy(isUploadingMedia = true) }
                val audioResult = repository.uploadMediaFile(state.audioUri)
                audioResult.fold(
                    onSuccess = { url -> audioUrl = url },
                    onFailure = { e ->
                        _uiState.update { it.copy(isSubmitting = false, isUploadingMedia = false, error = "Audio upload failed: ${e.message}") }
                        return@launch
                    }
                )
            }

            _uiState.update { it.copy(isUploadingMedia = false) }

            // Upload file if attached
            var uploadedFileUrl = state.uploadedFileUrl
            if (state.fileUri != null && uploadedFileUrl == null) {
                val fileResult = repository.uploadFile(state.fileUri)
                fileResult.fold(
                    onSuccess = { url -> uploadedFileUrl = url },
                    onFailure = { e ->
                        _uiState.update { it.copy(isSubmitting = false, error = "File upload failed: ${e.message}") }
                        return@launch
                    }
                )
            }


            val featureImage = uploadedImageUrls.firstOrNull()

            val mobiledoc = MobiledocBuilder.build(
                text = state.text,
                imageUrls = uploadedImageUrls,
                linkUrl = state.linkPreview?.url,
                linkTitle = state.linkPreview?.title,
                linkDescription = state.linkPreview?.description,
                imageAlt = altText,
                videoUrl = videoUrl,
                audioUrl = audioUrl,
                fileUrl = uploadedFileUrl,
                fileName = state.fileName,
                fileSize = state.fileSize ?: 0
            )
            val ghostTags = allTags.map { GhostTag(name = it) }

            val ghostPost = GhostPost(
                title = title,
                mobiledoc = mobiledoc,
                status = status.name.lowercase(),
                featured = if (status != PostStatus.DRAFT) state.featured else false,
                feature_image = featureImage,
                feature_image_alt = altText,
                published_at = state.scheduledAt,
                visibility = "public",
                tags = ghostTags.ifEmpty { null }
            )

            // Determine newsletter params
            val newsletterSlug = if (state.sendAsNewsletter && state.selectedNewsletter != null) {
                state.selectedNewsletter.slug
            } else null
            val emailSeg = if (newsletterSlug != null) state.emailSegment else null

            val result = if (editingGhostId != null) {
                val updatePost = ghostPost.copy(updated_at = editingUpdatedAt)
                repository.updatePost(editingGhostId!!, updatePost, newsletter = newsletterSlug, emailSegment = emailSeg)
            } else {
                repository.createPost(ghostPost, newsletter = newsletterSlug, emailSegment = emailSeg)
            }

            result.fold(
                onSuccess = {
                    // Clean up local copy if exists
                    editingLocalId?.let { repository.deleteLocalPost(it) }
                    _uiState.update { it.copy(isSubmitting = false, isSuccess = true) }
                },
                onFailure = { e ->
                    // Save to offline queue on failure
                    val localPost = LocalPost(
                        title = title,
                        content = state.text,
                        status = status,
                        featured = if (status != PostStatus.DRAFT) state.featured else false,
                        imageUri = state.imageUris.firstOrNull()?.toString(),
                        imageUris = Converters.stringListToJson(state.imageUris.map { it.toString() }),
                        uploadedImageUrl = featureImage,
                        uploadedImageUrls = Converters.stringListToJson(uploadedImageUrls),
                        imageAlt = altText,
                        videoUri = state.videoUri?.toString(),
                        uploadedVideoUrl = videoUrl,
                        audioUri = state.audioUri?.toString(),
                        uploadedAudioUrl = audioUrl,
                        linkUrl = state.linkPreview?.url,
                        linkTitle = state.linkPreview?.title,
                        linkDescription = state.linkPreview?.description,
                        linkImageUrl = state.linkPreview?.imageUrl,
                        scheduledAt = state.scheduledAt,
                        tags = tagsJson,
                        queueStatus = offlineQueueStatus,
                        fileUri = state.fileUri?.toString(),
                        uploadedFileUrl = uploadedFileUrl,
                        fileName = state.fileName,
                        newsletterSlug = if (state.sendAsNewsletter) state.selectedNewsletter?.slug else null
                    )
                    repository.saveLocalPost(localPost)
                    PostUploadWorker.enqueue(appContext)
                    _uiState.update { it.copy(isSubmitting = false, error = "Saved to queue: ${e.message}") }
                }
            )
        }
    }

    fun reset() {
        previewDebounceJob?.cancel()
        editingLocalId = null
        editingGhostId = null
        editingUpdatedAt = null
        _uiState.value = ComposerUiState()
    }

    companion object {
        const val PREVIEW_DEBOUNCE_MS = 500L
    }
}

data class ComposerUiState(
    val text: String = "",
    val imageUris: List<Uri> = emptyList(),
    val imageAlt: String = "",
    val videoUri: Uri? = null,
    val audioUri: Uri? = null,
    val uploadedVideoUrl: String? = null,
    val uploadedAudioUrl: String? = null,
    val isUploadingMedia: Boolean = false,
    val linkPreview: LinkPreview? = null,
    val isLoadingLink: Boolean = false,
    val scheduledAt: String? = null,
    val featured: Boolean = false,
    val extractedTags: List<String> = emptyList(),
    val availableTags: List<GhostTagFull> = emptyList(),
    val tagSuggestions: List<GhostTagFull> = emptyList(),
    val tagInput: String = "",
    val isSubmitting: Boolean = false,
    val isSuccess: Boolean = false,
    val isEditing: Boolean = false,
    val error: String? = null,
    val isPreviewMode: Boolean = false,
    val previewHtml: String = "",
    // File attachment
    val fileUri: Uri? = null,
    val fileName: String? = null,
    val fileSize: Long? = null,
    val fileMimeType: String? = null,
    val uploadedFileUrl: String? = null,
    // Newsletter fields
    val newsletterEnabled: Boolean = false,
    val availableNewsletters: List<GhostNewsletter> = emptyList(),
    val selectedNewsletter: GhostNewsletter? = null,
    val sendAsNewsletter: Boolean = false,
    val emailSegment: String = "all",
    val showNewsletterConfirmation: Boolean = false,
    val subscriberCount: Int? = null,
    // Email-only
    val showEmailOnlyConfirmation: Boolean = false
) {
    /**
     * Backwards compatibility: returns the first image URI or null.
     */
    val imageUri: Uri? get() = imageUris.firstOrNull()
}
