package main

import (
	"bytes"
	"encoding/json"
	"io"
	"net/http"
	"net/http/httptest"
	"testing"

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

func TestTagsFilter(t *testing.T) {
	setup := setupDeviceTest(t)
	defer setup.Server.Close()

	libraryID := setup.CreateLibrary(t, "Test Tags Library", "ebooks")
	_ = setup.CreateDevice(t, "Test Device", "koreader", "tags-test-123")

	client := &http.Client{}

	// Add folder to library (required before adding media items)
	folderReq := map[string]interface{}{
		"folder_path": "/app/uploads",
	}
	folderBody, _ := json.Marshal(folderReq)
	folderHTTP, _ := http.NewRequest("POST", setup.Server.URL+"/api/libraries/"+libraryID+"/folders", bytes.NewBuffer(folderBody))
	folderHTTP.Header.Set("Content-Type", "application/json")
	folderHTTP.Header.Set("Authorization", "Bearer "+setup.UserToken)
	folderResp, err := client.Do(folderHTTP)
	require.NoError(t, err)
	_ = folderResp.Body.Close()
	require.Equal(t, http.StatusCreated, folderResp.StatusCode, "Should add folder to library")

	// Helper to create book with tags
	createBook := func(title, author string, tags []string) {
		bookReq := map[string]interface{}{
			"library_id": libraryID,
			"title":      title,
			"author":     author,
			"tags":       tags,
			"file_path":  "/tmp/test.epub",
			"file_size":  1024,
			"mime_type":  "application/epub+zip",
		}
		body, _ := json.Marshal(bookReq)
		req, _ := http.NewRequest("POST", setup.Server.URL+"/api/media-items", bytes.NewBuffer(body))
		req.Header.Set("Content-Type", "application/json")
		req.Header.Set("Authorization", "Bearer "+setup.UserToken)
		resp, err := client.Do(req)
		require.NoError(t, err)
		defer func(Body io.ReadCloser) {
			_ = Body.Close()
		}(resp.Body)
		require.Equal(t, http.StatusCreated, resp.StatusCode, "Should create book")
	}

	// Create test books with different tags
	createBook("Foundation", "Asimov", []string{"Science Fiction", "Adventure"})
	createBook("Dune", "Herbert", []string{"Science Fiction", "Dystopian"})
	createBook("Neuromancer", "Gibson", []string{"Cyberpunk", "Science Fiction"})
	createBook("The Hobbit", "Tolkien", []string{"Fantasy", "Adventure"})

	t.Run("Tags filter - Science Fiction", func(t *testing.T) {
		req := httptest.NewRequest("GET", "/api/media-items/search?library_id="+libraryID+"&tags_filter=Science+Fiction", nil)
		req.Header.Set("Authorization", "Bearer "+setup.UserToken)
		rec := httptest.NewRecorder()
		setup.Server.Config.Handler.ServeHTTP(rec, req)

		assert.Equal(t, http.StatusOK, rec.Code, "Should return results")

		var results []map[string]interface{}
		err := json.Unmarshal(rec.Body.Bytes(), &results)
		require.NoError(t, err)
		assert.GreaterOrEqual(t, len(results), 3, "Should return Foundation, Dune, Neuromancer (all have 'Science Fiction' tag)")
	})

	t.Run("Tags filter - fuzzy match", func(t *testing.T) {
		req := httptest.NewRequest("GET", "/api/media-items/search?library_id="+libraryID+"&tags_filter=Sci+Fi", nil)
		req.Header.Set("Authorization", "Bearer "+setup.UserToken)
		rec := httptest.NewRecorder()
		setup.Server.Config.Handler.ServeHTTP(rec, req)

		assert.Equal(t, http.StatusOK, rec.Code, "Should return results")

		var results []map[string]interface{}
		err := json.Unmarshal(rec.Body.Bytes(), &results)
		require.NoError(t, err)
		assert.GreaterOrEqual(t, len(results), 3, "Should return Foundation, Dune, Neuromancer (fuzzy matches 'Science Fiction')")
	})

	t.Run("Tags filter - Fantasy", func(t *testing.T) {
		req := httptest.NewRequest("GET", "/api/media-items/search?library_id="+libraryID+"&tags_filter=Fantasy", nil)
		req.Header.Set("Authorization", "Bearer "+setup.UserToken)
		rec := httptest.NewRecorder()
		setup.Server.Config.Handler.ServeHTTP(rec, req)

		assert.Equal(t, http.StatusOK, rec.Code, "Should return results")

		var results []map[string]interface{}
		err := json.Unmarshal(rec.Body.Bytes(), &results)
		require.NoError(t, err)
		assert.Equal(t, 1, len(results), "Should return only The Hobbit")
	})

	t.Run("Tags autocomplete", func(t *testing.T) {
		req := httptest.NewRequest("GET", "/api/media-items/search?tags=Sci&library_id="+libraryID, nil)
		req.Header.Set("Authorization", "Bearer "+setup.UserToken)
		rec := httptest.NewRecorder()
		setup.Server.Config.Handler.ServeHTTP(rec, req)

		assert.Equal(t, http.StatusOK, rec.Code, "Should return autocomplete results")

		var response map[string]interface{}
		err := json.Unmarshal(rec.Body.Bytes(), &response)
		require.NoError(t, err)

		results := response["results"].([]interface{})
		assert.Greater(t, len(results), 0, "Should return 'Science Fiction' in autocomplete results")
	})

	t.Run("Backward compatibility - genre_filter still works", func(t *testing.T) {
		req := httptest.NewRequest("GET", "/api/media-items/search?library_id="+libraryID+"&genre_filter=Science+Fiction", nil)
		req.Header.Set("Authorization", "Bearer "+setup.UserToken)
		rec := httptest.NewRecorder()
		setup.Server.Config.Handler.ServeHTTP(rec, req)

		assert.Equal(t, http.StatusNotFound, rec.Code, "Should return 404 when no matches (genre is NULL for Calibre imports)")
	})

	t.Run("Combined filters - tags + author", func(t *testing.T) {
		req := httptest.NewRequest("GET", "/api/media-items/search?library_id="+libraryID+"&tags_filter=Sci+Fi&author_filter=Asimov", nil)
		req.Header.Set("Authorization", "Bearer "+setup.UserToken)
		rec := httptest.NewRecorder()
		setup.Server.Config.Handler.ServeHTTP(rec, req)

		assert.Equal(t, http.StatusOK, rec.Code, "Should return results")

		var results []map[string]interface{}
		err := json.Unmarshal(rec.Body.Bytes(), &results)
		require.NoError(t, err)
		assert.Equal(t, 1, len(results), "Should return only Foundation")
	})
}
