// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package validator // import "miniflux.app/v2/internal/validator"

import (
	"testing"

	"miniflux.app/v2/internal/locale"
	"miniflux.app/v2/internal/model"
)

func TestValidateUsername(t *testing.T) {
	scenarios := map[string]*locale.LocalizedError{
		"userone":          nil,
		"user.name":        nil,
		"user@example.com": nil,
		"john_doe":         nil,
		"john-doe":         nil,
		"User123":          nil,
		"invalid username": locale.NewLocalizedError("error.invalid_username"),
		"user/path":        locale.NewLocalizedError("error.invalid_username"),
		"user🙂":            locale.NewLocalizedError("error.invalid_username"),
	}

	for username, expected := range scenarios {
		result := validateUsername(username)
		if expected == nil {
			if result != nil {
				t.Errorf(`got an unexpected error for %q instead of nil: %v`, username, result)
			}
		} else {
			if result == nil {
				t.Errorf(`expected an error, got nil.`)
			}
		}
	}
}

func TestValidateReadingSpeed(t *testing.T) {
	tests := map[int]bool{
		1:   false,
		100: false,
		0:   true,
		-5:  true,
	}

	for speed, wantErr := range tests {
		if err := validateReadingSpeed(speed); (err != nil) != wantErr {
			t.Errorf("reading speed %d error mismatch: got %v wantErr %v", speed, err, wantErr)
		}
	}
}

func TestValidatePassword(t *testing.T) {
	tests := map[string]bool{
		"secret":   false,
		"longpass": false,
		"short":    true,
		"":         true,
	}

	for password, wantErr := range tests {
		if err := validatePassword(password); (err != nil) != wantErr {
			t.Errorf("password %q error mismatch: got %v wantErr %v", password, err, wantErr)
		}
	}
}

func TestValidateTheme(t *testing.T) {
	if err := validateTheme("light_serif"); err != nil {
		t.Errorf("expected valid theme to pass, got %v", err)
	}

	if err := validateTheme("unknown"); err == nil {
		t.Error("expected invalid theme to fail")
	}
}

func TestValidateLanguage(t *testing.T) {
	if err := validateLanguage("en_US"); err != nil {
		t.Errorf("expected valid language to pass, got %v", err)
	}

	if err := validateLanguage("xx_YY"); err == nil {
		t.Error("expected invalid language to fail")
	}
}

func TestValidateTimezone(t *testing.T) {
	if err := validateTimezone("UTC"); err != nil {
		t.Errorf("expected valid timezone to pass, got %v", err)
	}

	if err := validateTimezone("Invalid/Zone"); err == nil {
		t.Error("expected invalid timezone to fail")
	}
}

func TestValidateEntryDirection(t *testing.T) {
	for _, direction := range []string{"asc", "desc"} {
		if err := ValidateDirection(direction); err != nil {
			t.Errorf("expected valid direction %q to pass, got %v", direction, err)
		}
	}

	if err := ValidateDirection("sideways"); err == nil {
		t.Error("expected invalid direction to fail")
	}
}

func TestValidateEntriesPerPage(t *testing.T) {
	for _, value := range []int{1, model.MaxEntryLimit} {
		if err := validateEntriesPerPage(value); err != nil {
			t.Errorf("expected %d to pass, got %v", value, err)
		}
	}

	for _, value := range []int{0, -1, model.MaxEntryLimit + 1} {
		if err := validateEntriesPerPage(value); err == nil {
			t.Errorf("expected %d to fail", value)
		}
	}
}

func TestValidateEntrySortingOrder(t *testing.T) {
	for _, order := range []string{"published_at", "created_at"} {
		if err := validateEntrySortingOrder(order); err != nil {
			t.Errorf("expected valid order %q to pass, got %v", order, err)
		}
	}

	// Valid entry-listing orders that are not part of the entry_sorting_order enum.
	for _, order := range []string{"id", "status", "changed_at", "category_title", "category_id", "title", "author"} {
		if err := validateEntrySortingOrder(order); err == nil {
			t.Errorf("expected order %q to be rejected", order)
		}
	}

	if err := validateEntrySortingOrder("invalid"); err == nil {
		t.Error("expected invalid order to fail")
	}
}

func TestValidateCategoriesSortingOrder(t *testing.T) {
	for _, order := range []string{"alphabetical", "unread_count"} {
		if err := validateCategoriesSortingOrder(order); err != nil {
			t.Errorf("expected valid order %q to pass, got %v", order, err)
		}
	}

	if err := validateCategoriesSortingOrder("popularity"); err == nil {
		t.Error("expected invalid order to fail")
	}
}

func TestValidateDisplayMode(t *testing.T) {
	for _, mode := range []string{"fullscreen", "standalone", "minimal-ui", "browser"} {
		if err := validateDisplayMode(mode); err != nil {
			t.Errorf("expected valid mode %q to pass, got %v", mode, err)
		}
	}

	if err := validateDisplayMode("windowed"); err == nil {
		t.Error("expected invalid display mode to fail")
	}
}

func TestValidateGestureNav(t *testing.T) {
	for _, gesture := range []string{"none", "tap", "swipe"} {
		if err := validateGestureNav(gesture); err != nil {
			t.Errorf("expected valid gesture %q to pass, got %v", gesture, err)
		}
	}

	if err := validateGestureNav("pinch"); err == nil {
		t.Error("expected invalid gesture to fail")
	}
}

func TestValidateDefaultHomePage(t *testing.T) {
	if err := validateDefaultHomePage("unread"); err != nil {
		t.Errorf("expected valid home page to pass, got %v", err)
	}

	if err := validateDefaultHomePage("dashboard"); err == nil {
		t.Error("expected invalid home page to fail")
	}
}

func TestValidateMediaPlaybackRate(t *testing.T) {
	for _, rate := range []float64{0.25, 1.0, 4.0} {
		if err := validateMediaPlaybackRate(rate); err != nil {
			t.Errorf("expected valid rate %.2f to pass, got %v", rate, err)
		}
	}

	for _, rate := range []float64{0.1, 4.1} {
		if err := validateMediaPlaybackRate(rate); err == nil {
			t.Errorf("expected invalid rate %.2f to fail", rate)
		}
	}
}

func TestValidateUserModificationAllowsClearingFilterRules(t *testing.T) {
	req := &model.UserModificationRequest{
		BlockFilterEntryRules: new(string),
		KeepFilterEntryRules:  new(string),
	}

	if err := ValidateUserModification(nil, 0, req); err != nil {
		t.Fatalf("expected empty filter rules to be accepted, got %v", err)
	}
}

func TestValidateUserModificationRejectsInvalidNonEmptyFilterRule(t *testing.T) {
	req := &model.UserModificationRequest{
		BlockFilterEntryRules: new("EntryTitle=["),
	}

	if err := ValidateUserModification(nil, 0, req); err == nil {
		t.Fatal("expected invalid non-empty filter rules to be rejected")
	}
}
