import Foundation
import Observation

@Observable
final class PageEditorViewModel {

    enum Mode {
        case create(bookId: Int, chapterId: Int? = nil)
        case edit(page: PageDTO)
    }

    enum EditorTab {
        case write, preview
    }

    enum ImageUploadState {
        case idle
        case uploading
        case failed(String)
    }

    var mode: Mode
    var title: String = ""
    var markdownContent: String = ""
    var activeTab: EditorTab = .write

    var isSaving: Bool = false
    var saveError: BookStackError? = nil
    var savedPage: PageDTO? = nil

    var imageUploadState: ImageUploadState = .idle
    /// Set by the view after a successful upload so the markdown can be inserted at the cursor
    var pendingImageMarkdown: String? = nil

    // MARK: - Tags
    var tags: [TagDTO] = []
    var availableTags: [TagDTO] = []
    var isLoadingTags: Bool = false

    // Snapshot of content at last save (or initial load), used to detect unsaved changes
    private var lastSavedTitle: String = ""
    private var lastSavedMarkdown: String = ""
    private var lastSavedTags: [TagDTO] = []

    var hasUnsavedChanges: Bool {
        title != lastSavedTitle
            || markdownContent != lastSavedMarkdown
            || tags != lastSavedTags
    }

    init(mode: Mode) {
        self.mode = mode
        if case .edit(let page) = mode {
            title = page.name
            markdownContent = page.markdown ?? ""
            tags = page.tags
        }
        // Snapshot the initial state so "no changes yet" returns false
        lastSavedTitle    = title
        lastSavedMarkdown = markdownContent
        lastSavedTags     = tags
    }

    func loadAvailableTags() async {
        isLoadingTags = true
        do {
            availableTags = try await BookStackAPI.shared.fetchTags()
        } catch {
            // Non-fatal — autocomplete just won't show suggestions
            AppLog(.warning, "Could not load tags: \(error.localizedDescription)", category: "Editor")
        }
        isLoadingTags = false
    }

    func addTag(name: String, value: String = "") {
        let trimmedName = name.trimmingCharacters(in: .whitespacesAndNewlines)
        let trimmedValue = value.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !trimmedName.isEmpty else { return }
        // Avoid duplicates
        guard !tags.contains(where: { $0.name == trimmedName && $0.value == trimmedValue }) else { return }
        tags.append(TagDTO(name: trimmedName, value: trimmedValue, order: tags.count))
    }

    func removeTag(_ tag: TagDTO) {
        tags.removeAll { $0.id == tag.id }
    }

    // MARK: - Save

    func save() async {
        guard !title.isEmpty, !markdownContent.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { return }
        isSaving = true
        saveError = nil

        do {
            switch mode {
            case .create(let bookId, let chapterId):
                AppLog(.info, "Creating page '\(title)' in book \(bookId)", category: "Editor")
                let created = try await BookStackAPI.shared.createPage(
                    bookId: bookId,
                    chapterId: chapterId,
                    name: title,
                    markdown: markdownContent,
                    tags: tags
                )
                savedPage = created
                // Switch to edit mode so subsequent saves update rather than duplicate
                mode = .edit(page: created)
                AppLog(.info, "Page '\(title)' created (id: \(created.id))", category: "Editor")
            case .edit(let page):
                AppLog(.info, "Saving edits to page '\(title)' (id: \(page.id))", category: "Editor")
                savedPage = try await BookStackAPI.shared.updatePage(
                    id: page.id,
                    name: title,
                    markdown: markdownContent,
                    tags: tags
                )
                AppLog(.info, "Page '\(title)' saved successfully", category: "Editor")
            }
            // Update snapshot so closing immediately after saving doesn't trigger the alert
            lastSavedTitle    = title
            lastSavedMarkdown = markdownContent
            lastSavedTags     = tags
        } catch let e as BookStackError {
            AppLog(.error, "Save failed for '\(title)': \(e.localizedDescription)", category: "Editor")
            saveError = e
        } catch {
            AppLog(.error, "Save failed for '\(title)': \(error.localizedDescription)", category: "Editor")
            saveError = .unknown(error.localizedDescription)
        }

        isSaving = false
    }

    // MARK: - Image Upload

    /// The page ID to use when uploading images.
    /// For new pages, we use 0 (BookStack accepts unattached images).
    var uploadTargetPageId: Int {
        if case .edit(let page) = mode { return page.id }
        return 0
    }

    func uploadImage(data: Data, filename: String, mimeType: String) async {
        imageUploadState = .uploading
        do {
            let result = try await BookStackAPI.shared.uploadImage(
                data: data,
                filename: filename,
                mimeType: mimeType,
                pageId: uploadTargetPageId
            )
            // Use the markdown embed string from the API response, or build one from the URL
            let markdown = result.content?.markdown ?? "![\(result.name)](\(result.url))"
            pendingImageMarkdown = markdown
            imageUploadState = .idle
            AppLog(.info, "Image '\(filename)' uploaded successfully", category: "Editor")
        } catch {
            let msg = (error as? BookStackError)?.localizedDescription ?? error.localizedDescription
            AppLog(.error, "Image upload failed: \(msg)", category: "Editor")
            imageUploadState = .failed(msg)
        }
    }
}
