import Testing
@testable import bookstax
import Foundation

// MARK: - Helpers

private func makePageDTO(markdown: String? = "# Hello", tags: [TagDTO] = []) -> PageDTO {
    PageDTO(
        id: 42,
        bookId: 1,
        chapterId: nil,
        name: "Test Page",
        slug: "test-page",
        html: nil,
        markdown: markdown,
        priority: 0,
        draftStatus: false,
        tags: tags,
        createdAt: Date(),
        updatedAt: Date()
    )
}

// MARK: - Initialisation

@Suite("PageEditorViewModel – Initialisation")
struct PageEditorViewModelInitTests {

    @Test("Create mode starts with empty title and content")
    func createModeDefaults() {
        let vm = PageEditorViewModel(mode: .create(bookId: 1))
        #expect(vm.title.isEmpty)
        #expect(vm.markdownContent.isEmpty)
        #expect(vm.tags.isEmpty)
        #expect(vm.isHtmlOnlyPage == false)
    }

    @Test("Edit mode populates title and markdown from page")
    func editModePopulates() {
        let page = makePageDTO(markdown: "## Content")
        let vm = PageEditorViewModel(mode: .edit(page: page))
        #expect(vm.title == "Test Page")
        #expect(vm.markdownContent == "## Content")
        #expect(vm.isHtmlOnlyPage == false)
    }

    @Test("Edit mode with nil markdown sets isHtmlOnlyPage")
    func htmlOnlyPage() {
        let page = makePageDTO(markdown: nil)
        let vm = PageEditorViewModel(mode: .edit(page: page))
        #expect(vm.isHtmlOnlyPage == true)
        #expect(vm.markdownContent.isEmpty)
    }

    @Test("Edit mode with existing tags loads them")
    func editModeLoadsTags() {
        let tags = [TagDTO(name: "topic", value: "swift", order: 0)]
        let page = makePageDTO(tags: tags)
        let vm = PageEditorViewModel(mode: .edit(page: page))
        #expect(vm.tags.count == 1)
        #expect(vm.tags.first?.name == "topic")
    }
}

// MARK: - hasUnsavedChanges

@Suite("PageEditorViewModel – hasUnsavedChanges")
struct PageEditorViewModelUnsavedTests {

    @Test("No changes after init → hasUnsavedChanges is false")
    func noChangesAfterInit() {
        let vm = PageEditorViewModel(mode: .create(bookId: 1))
        #expect(vm.hasUnsavedChanges == false)
    }

    @Test("Changing title → hasUnsavedChanges is true")
    func titleChange() {
        let vm = PageEditorViewModel(mode: .create(bookId: 1))
        vm.title = "New Title"
        #expect(vm.hasUnsavedChanges == true)
    }

    @Test("Changing markdownContent → hasUnsavedChanges is true")
    func contentChange() {
        let vm = PageEditorViewModel(mode: .create(bookId: 1))
        vm.markdownContent = "Some text"
        #expect(vm.hasUnsavedChanges == true)
    }

    @Test("Adding a tag → hasUnsavedChanges is true")
    func tagAddition() {
        let page = makePageDTO()
        let vm = PageEditorViewModel(mode: .edit(page: page))
        vm.addTag(name: "new-tag")
        #expect(vm.hasUnsavedChanges == true)
    }

    @Test("Restoring original values → hasUnsavedChanges is false again")
    func revertChanges() {
        let vm = PageEditorViewModel(mode: .create(bookId: 1))
        vm.title = "Changed"
        vm.title = ""
        #expect(vm.hasUnsavedChanges == false)
    }
}

// MARK: - isSaveDisabled

@Suite("PageEditorViewModel – isSaveDisabled")
struct PageEditorViewModelSaveDisabledTests {

    @Test("Empty title disables save in create mode")
    func emptyTitleCreate() {
        let vm = PageEditorViewModel(mode: .create(bookId: 1))
        vm.markdownContent = "Some content"
        #expect(vm.isSaveDisabled == true)
    }

    @Test("Empty content disables save in create mode even if title is set")
    func emptyContentCreate() {
        let vm = PageEditorViewModel(mode: .create(bookId: 1))
        vm.title = "My Page"
        vm.markdownContent = ""
        #expect(vm.isSaveDisabled == true)
    }

    @Test("Whitespace-only content disables save in create mode")
    func whitespaceContentCreate() {
        let vm = PageEditorViewModel(mode: .create(bookId: 1))
        vm.title = "My Page"
        vm.markdownContent = "   \n  "
        #expect(vm.isSaveDisabled == true)
    }

    @Test("Title and content both set enables save in create mode")
    func validCreate() {
        let vm = PageEditorViewModel(mode: .create(bookId: 1))
        vm.title = "My Page"
        vm.markdownContent = "Hello world"
        #expect(vm.isSaveDisabled == false)
    }

    @Test("Edit mode only requires title – empty content is allowed")
    func editOnlyNeedsTitle() {
        let page = makePageDTO(markdown: nil)
        let vm = PageEditorViewModel(mode: .edit(page: page))
        vm.title = "Existing Page"
        vm.markdownContent = ""
        #expect(vm.isSaveDisabled == false)
    }

    @Test("Empty title disables save in edit mode")
    func emptyTitleEdit() {
        let page = makePageDTO()
        let vm = PageEditorViewModel(mode: .edit(page: page))
        vm.title = ""
        #expect(vm.isSaveDisabled == true)
    }
}

// MARK: - Tag Management

@Suite("PageEditorViewModel – Tags")
struct PageEditorViewModelTagTests {

    @Test("addTag appends a new tag")
    func addTag() {
        let vm = PageEditorViewModel(mode: .create(bookId: 1))
        vm.addTag(name: "status", value: "draft")
        #expect(vm.tags.count == 1)
        #expect(vm.tags.first?.name == "status")
        #expect(vm.tags.first?.value == "draft")
    }

    @Test("addTag trims whitespace from name and value")
    func addTagTrims() {
        let vm = PageEditorViewModel(mode: .create(bookId: 1))
        vm.addTag(name: "  topic  ", value: "  swift  ")
        #expect(vm.tags.first?.name == "topic")
        #expect(vm.tags.first?.value == "swift")
    }

    @Test("addTag with empty name after trimming is ignored")
    func addTagEmptyNameIgnored() {
        let vm = PageEditorViewModel(mode: .create(bookId: 1))
        vm.addTag(name: "   ")
        #expect(vm.tags.isEmpty)
    }

    @Test("addTag prevents duplicate (same name + value) entries")
    func addTagNoDuplicates() {
        let vm = PageEditorViewModel(mode: .create(bookId: 1))
        vm.addTag(name: "lang", value: "swift")
        vm.addTag(name: "lang", value: "swift")
        #expect(vm.tags.count == 1)
    }

    @Test("addTag allows same name with different value")
    func addTagSameNameDifferentValue() {
        let vm = PageEditorViewModel(mode: .create(bookId: 1))
        vm.addTag(name: "env", value: "dev")
        vm.addTag(name: "env", value: "prod")
        #expect(vm.tags.count == 2)
    }

    @Test("removeTag removes the matching tag by id")
    func removeTag() {
        let vm = PageEditorViewModel(mode: .create(bookId: 1))
        vm.addTag(name: "remove-me", value: "yes")
        let tag = vm.tags[0]
        vm.removeTag(tag)
        #expect(vm.tags.isEmpty)
    }

    @Test("removeTag does not remove non-matching tags")
    func removeTagKeepsOthers() {
        let vm = PageEditorViewModel(mode: .create(bookId: 1))
        vm.addTag(name: "keep", value: "")
        vm.addTag(name: "remove", value: "")
        let toRemove = vm.tags.first { $0.name == "remove" }!
        vm.removeTag(toRemove)
        #expect(vm.tags.count == 1)
        #expect(vm.tags.first?.name == "keep")
    }
}

// MARK: - uploadTargetPageId

@Suite("PageEditorViewModel – uploadTargetPageId")
struct PageEditorViewModelUploadIDTests {

    @Test("Create mode returns 0 as upload target")
    func createModeUploadTarget() {
        let vm = PageEditorViewModel(mode: .create(bookId: 5))
        #expect(vm.uploadTargetPageId == 0)
    }

    @Test("Edit mode returns the existing page id")
    func editModeUploadTarget() {
        let page = makePageDTO()
        let vm = PageEditorViewModel(mode: .edit(page: page))
        #expect(vm.uploadTargetPageId == 42)
    }
}
