package book

import (
	"strings"
	"testing"

	"github.com/flipped-aurora/gin-vue-admin/server/model/book"
)

func TestValidateBookRejectsOutOfRangeAggregates(t *testing.T) {
	tests := []struct {
		name string
		item book.Book
		want string
	}{
		{name: "negative hot score", item: book.Book{HotScore: -1}, want: "hotScore"},
		{name: "rating below zero", item: book.Book{Rating: -0.1}, want: "rating"},
		{name: "rating above ten", item: book.Book{Rating: 10.1}, want: "rating"},
		{name: "negative comment count", item: book.Book{CommentCount: -1}, want: "commentCount"},
		{name: "negative word count", item: book.Book{WordCount: -1}, want: "wordCount"},
		{name: "negative series sort", item: book.Book{SeriesSort: -1}, want: "seriesSort"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			assertValidationErrorContains(t, validateBook(tt.item), tt.want)
		})
	}
}

func TestValidateBookRejectsInvalidFixedDictionaryValues(t *testing.T) {
	tests := []struct {
		name string
		item book.Book
		want string
	}{
		{name: "invalid era tag", item: book.Book{EraTag: "bad", CompletionStatus: book.BookCompletionStatusSerializing, PublishStatus: book.BookPublishStatusDraft}, want: "eraTag"},
		{name: "invalid completion status", item: book.Book{EraTag: book.BookEraTagUnknown, CompletionStatus: "bad", PublishStatus: book.BookPublishStatusDraft}, want: "completionStatus"},
		{name: "invalid publish status", item: book.Book{EraTag: book.BookEraTagUnknown, CompletionStatus: book.BookCompletionStatusSerializing, PublishStatus: "bad"}, want: "publishStatus"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			assertValidationErrorContains(t, validateBook(tt.item), tt.want)
		})
	}
}

func TestValidateBookAuthorRejectsInvalidStatus(t *testing.T) {
	err := validateBookAuthor(book.BookAuthor{AuthorStatus: "bad"})
	assertValidationErrorContains(t, err, "authorStatus")
}

func TestApplyBookAuthorRelationDefaults(t *testing.T) {
	item := applyBookAuthorRelationDefaults(book.BookAuthorRelation{})
	if item.AuthorSort != 1 {
		t.Fatalf("AuthorSort = %d, want 1", item.AuthorSort)
	}
}

func TestValidateBookChapterRejectsReadableWithoutLines(t *testing.T) {
	err := validateBookChapter(book.BookChapter{ChapterNo: 1, IsReadable: true, TotalLines: 0})
	assertValidationErrorContains(t, err, "totalLines")
}

func TestValidateBookAuthorRelationRejectsInvalidSort(t *testing.T) {
	err := validateBookAuthorRelation(book.BookAuthorRelation{AuthorSort: 0})
	assertValidationErrorContains(t, err, "authorSort")
}

func TestValidateBookCommentRejectsInvalidAnchorLikeCountAndStatus(t *testing.T) {
	tests := []struct {
		name string
		item book.BookComment
		want string
	}{
		{name: "book comment with line", item: book.BookComment{ChapterID: 0, LineIndex: 1}, want: "lineIndex"},
		{name: "negative like count", item: book.BookComment{LikeCount: -1}, want: "likeCount"},
		{name: "invalid comment status", item: book.BookComment{CommentStatus: "bad"}, want: "commentStatus"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			assertValidationErrorContains(t, validateBookComment(tt.item), tt.want)
		})
	}
}

func TestValidateBookReadRecordRejectsInvalidProgressAndAnchor(t *testing.T) {
	tests := []struct {
		name string
		item book.BookReadRecord
		want string
	}{
		{name: "progress below zero", item: book.BookReadRecord{ReadProgress: -0.1, ChapterID: 1, LineIndex: 1}, want: "readProgress"},
		{name: "progress above one hundred", item: book.BookReadRecord{ReadProgress: 100.1, ChapterID: 1, LineIndex: 1}, want: "readProgress"},
		{name: "missing chapter", item: book.BookReadRecord{ReadProgress: 10, ChapterID: 0, LineIndex: 1}, want: "chapterId"},
		{name: "missing line", item: book.BookReadRecord{ReadProgress: 10, ChapterID: 1, LineIndex: 0}, want: "lineIndex"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			assertValidationErrorContains(t, validateBookReadRecord(tt.item), tt.want)
		})
	}
}

func assertValidationErrorContains(t *testing.T, err error, want string) {
	t.Helper()
	if err == nil {
		t.Fatalf("validation error = nil, want %q", want)
	}
	if !strings.Contains(err.Error(), want) {
		t.Fatalf("validation error = %q, want contains %q", err.Error(), want)
	}
}
