package handlers

import (
	"bookhoard/internal/opds"
	"strings"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

// rels collects the rel attributes of all links currently on the feed.
func rels(feed *opds.Feed) []string {
	out := make([]string, 0, len(feed.Links))
	for _, l := range feed.Links {
		out = append(out, l.Rel)
	}
	return out
}

func containsRel(feed *opds.Feed, rel string) bool {
	for _, l := range feed.Links {
		if l.Rel == rel {
			return true
		}
	}
	return false
}

func TestAddCatalogPaginationLinks_MiddlePage(t *testing.T) {
	feed := opds.NewFeed("urn:uuid:dev", "Library")
	// 1814 items, 50 per page => 37 pages; on page 2
	addCatalogPaginationLinks(feed, "http://h/opds/devices/dev/catalog", 2, 50, 1814, "tok")

	assert.True(t, containsRel(feed, "self"))
	assert.True(t, containsRel(feed, "start"))
	assert.True(t, containsRel(feed, "first"))
	assert.True(t, containsRel(feed, "last"))
	assert.True(t, containsRel(feed, "previous"), "middle page must have previous")
	assert.True(t, containsRel(feed, "next"), "middle page must have next")

	// self must point to the current page
	var selfHref string
	for _, l := range feed.Links {
		if l.Rel == "self" {
			selfHref = l.Href
		}
	}
	assert.Contains(t, selfHref, "page=2&per_page=50")
	assert.Contains(t, selfHref, "token=tok")

	// next must advance the page
	var nextHref string
	for _, l := range feed.Links {
		if l.Rel == "next" {
			nextHref = l.Href
		}
	}
	assert.Contains(t, nextHref, "page=3")

	// OpenSearch metadata
	require.NotNil(t, feed.TotalResults)
	assert.Equal(t, 1814, *feed.TotalResults)
	require.NotNil(t, feed.ItemsPerPage)
	assert.Equal(t, 50, *feed.ItemsPerPage)
	require.NotNil(t, feed.StartIndex)
	assert.Equal(t, 51, *feed.StartIndex, "startIndex should be 1-based offset of first item on page 2")
}

func TestAddCatalogPaginationLinks_FirstPage_NoPrevious(t *testing.T) {
	feed := opds.NewFeed("urn:uuid:dev", "Library")
	addCatalogPaginationLinks(feed, "http://h/opds/devices/dev/catalog", 1, 50, 1814, "")

	rels := rels(feed)
	assert.NotContains(t, rels, "previous", "first page must not have previous")
	assert.Contains(t, rels, "next")
}

func TestAddCatalogPaginationLinks_LastPage_NoNext(t *testing.T) {
	feed := opds.NewFeed("urn:uuid:dev", "Library")
	addCatalogPaginationLinks(feed, "http://h/opds/devices/dev/catalog", 37, 50, 1814, "")

	rels := rels(feed)
	assert.NotContains(t, rels, "next", "last page must not have next")
	assert.Contains(t, rels, "previous")
}

func TestAddCatalogPaginationLinks_SinglePage(t *testing.T) {
	feed := opds.NewFeed("urn:uuid:dev", "Library")
	addCatalogPaginationLinks(feed, "http://h/opds/devices/dev/catalog", 1, 50, 10, "")

	rels := rels(feed)
	assert.NotContains(t, rels, "previous")
	assert.NotContains(t, rels, "next")
	// still emits self/start/first/last
	assert.Contains(t, rels, "self")
	assert.Contains(t, rels, "last")
}

func TestAddCatalogPaginationLinks_EmptyCatalog(t *testing.T) {
	feed := opds.NewFeed("urn:uuid:dev", "Library")
	addCatalogPaginationLinks(feed, "http://h/opds/devices/dev/catalog", 1, 50, 0, "")

	rels := rels(feed)
	assert.NotContains(t, rels, "next")
	assert.NotContains(t, rels, "previous")
	assert.NotContains(t, rels, "last", "empty catalog should not advertise a last page")
	require.NotNil(t, feed.TotalResults)
	assert.Equal(t, 0, *feed.TotalResults)
}

func TestAddCatalogPaginationLinks_TokenAppended(t *testing.T) {
	feed := opds.NewFeed("urn:uuid:dev", "Library")
	addCatalogPaginationLinks(feed, "http://h/opds/devices/dev/catalog", 1, 50, 100, "abc")

	xml, err := feed.GenerateXMLString()
	require.NoError(t, err)
	assert.True(t, strings.Count(xml, "token=abc") >= 3, "token should be appended to generated links")
}
