package main

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

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

func TestSavedFilters(t *testing.T) {
	setup := setupTestServer(t)
	client := &http.Client{}

	t.Run("GET /api/saved-filters without auth returns 401", func(t *testing.T) {
		httpReq, _ := http.NewRequest("GET", setup.Server.URL+"/api/saved-filters?resource_type=media-items", nil)
		resp, err := client.Do(httpReq)
		require.NoError(t, err)
		defer func(Body io.ReadCloser) {
			_ = Body.Close()
		}(resp.Body)

		assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
	})

	t.Run("GET /api/saved-filters with user auth returns empty array initially", func(t *testing.T) {
		httpReq, _ := http.NewRequest("GET", setup.Server.URL+"/api/saved-filters?resource_type=media-items", nil)
		httpReq.Header.Set("Authorization", "Bearer "+setup.Token)

		resp, err := client.Do(httpReq)
		require.NoError(t, err)
		defer func(Body io.ReadCloser) {
			_ = Body.Close()
		}(resp.Body)

		assert.Equal(t, http.StatusOK, resp.StatusCode)

		var filters []map[string]interface{}
		err = json.NewDecoder(resp.Body).Decode(&filters)
		require.NoError(t, err)
		assert.Equal(t, 0, len(filters))
	})

	t.Run("POST /api/saved-filters creates filter", func(t *testing.T) {
		reqBody := map[string]interface{}{
			"name":          "My Sci-Fi Books",
			"resource_type": "media-items",
			"filters": map[string]string{
				"genre_filter": "Science Fiction",
				"sort":         "title ASC",
			},
		}
		body, _ := json.Marshal(reqBody)

		httpReq, _ := http.NewRequest("POST", setup.Server.URL+"/api/saved-filters", bytes.NewBuffer(body))
		httpReq.Header.Set("Content-Type", "application/json")
		httpReq.Header.Set("Authorization", "Bearer "+setup.Token)

		resp, err := client.Do(httpReq)
		require.NoError(t, err)
		defer func(Body io.ReadCloser) {
			_ = Body.Close()
		}(resp.Body)

		assert.Equal(t, http.StatusCreated, resp.StatusCode)

		var filter map[string]interface{}
		err = json.NewDecoder(resp.Body).Decode(&filter)
		require.NoError(t, err)
		assert.Equal(t, "My Sci-Fi Books", filter["name"])
		assert.Equal(t, "media-items", filter["resource_type"])
		assert.NotEmpty(t, filter["id"])
		// Filters field will be JSON object (JSONB as raw bytes)
		assert.NotEmpty(t, filter["filters"])
	})

	t.Run("POST /api/saved-filters with duplicate name returns 409", func(t *testing.T) {
		reqBody := map[string]interface{}{
			"name":          "Duplicate Test",
			"resource_type": "media-items",
			"filters":       map[string]string{"search": "test"},
		}
		body, _ := json.Marshal(reqBody)

		// First request
		httpReq1, _ := http.NewRequest("POST", setup.Server.URL+"/api/saved-filters", bytes.NewBuffer(body))
		httpReq1.Header.Set("Content-Type", "application/json")
		httpReq1.Header.Set("Authorization", "Bearer "+setup.Token)

		resp1, err := client.Do(httpReq1)
		require.NoError(t, err)
		_ = resp1.Body.Close()

		assert.Equal(t, http.StatusCreated, resp1.StatusCode)

		// Second request with same name
		body2, _ := json.Marshal(reqBody)
		httpReq2, _ := http.NewRequest("POST", setup.Server.URL+"/api/saved-filters", bytes.NewBuffer(body2))
		httpReq2.Header.Set("Content-Type", "application/json")
		httpReq2.Header.Set("Authorization", "Bearer "+setup.Token)

		resp2, err := client.Do(httpReq2)
		require.NoError(t, err)
		_ = resp2.Body.Close()

		assert.Equal(t, http.StatusConflict, resp2.StatusCode)
	})

	t.Run("PUT /api/saved-filters/:id updates filter", func(t *testing.T) {
		// Create filter first
		createReq := map[string]interface{}{
			"name":          "Original Name",
			"resource_type": "media-items",
			"filters":       map[string]string{"search": "original"},
		}
		body, _ := json.Marshal(createReq)

		createHTTP, _ := http.NewRequest("POST", setup.Server.URL+"/api/saved-filters", bytes.NewBuffer(body))
		createHTTP.Header.Set("Content-Type", "application/json")
		createHTTP.Header.Set("Authorization", "Bearer "+setup.Token)

		createResp, err := client.Do(createHTTP)
		require.NoError(t, err)
		defer func(Body io.ReadCloser) {
			_ = Body.Close()
		}(createResp.Body)

		assert.Equal(t, http.StatusCreated, createResp.StatusCode)

		var createdFilter map[string]interface{}
		err = json.NewDecoder(createResp.Body).Decode(&createdFilter)
		require.NoError(t, err)
		filterID := createdFilter["id"].(string)

		// Update filter
		updateReq := map[string]interface{}{
			"name":          "Updated Name",
			"resource_type": "media-items",
			"filters":       map[string]string{"search": "updated"},
		}
		updateBody, _ := json.Marshal(updateReq)

		updateHTTP, _ := http.NewRequest("PUT", setup.Server.URL+"/api/saved-filters/"+filterID, bytes.NewBuffer(updateBody))
		updateHTTP.Header.Set("Content-Type", "application/json")
		updateHTTP.Header.Set("Authorization", "Bearer "+setup.Token)

		updateResp, err := client.Do(updateHTTP)
		require.NoError(t, err)
		defer func(Body io.ReadCloser) {
			_ = Body.Close()
		}(updateResp.Body)

		assert.Equal(t, http.StatusOK, updateResp.StatusCode)

		var updatedFilter map[string]interface{}
		err = json.NewDecoder(updateResp.Body).Decode(&updatedFilter)
		require.NoError(t, err)
		assert.Equal(t, "Updated Name", updatedFilter["name"])
	})

	t.Run("DELETE /api/saved-filters/:id deletes filter", func(t *testing.T) {
		// Create filter
		createReq := map[string]interface{}{
			"name":          "To Be Deleted",
			"resource_type": "media-items",
			"filters":       map[string]string{},
		}
		body, _ := json.Marshal(createReq)

		createHTTP, _ := http.NewRequest("POST", setup.Server.URL+"/api/saved-filters", bytes.NewBuffer(body))
		createHTTP.Header.Set("Content-Type", "application/json")
		createHTTP.Header.Set("Authorization", "Bearer "+setup.Token)

		createResp, err := client.Do(createHTTP)
		require.NoError(t, err)
		defer func(Body io.ReadCloser) {
			_ = Body.Close()
		}(createResp.Body)

		var createdFilter map[string]interface{}
		err = json.NewDecoder(createResp.Body).Decode(&createdFilter)
		require.NoError(t, err)
		filterID := createdFilter["id"].(string)

		// Delete filter
		deleteHTTP, _ := http.NewRequest("DELETE", setup.Server.URL+"/api/saved-filters/"+filterID, nil)
		deleteHTTP.Header.Set("Authorization", "Bearer "+setup.Token)

		deleteResp, err := client.Do(deleteHTTP)
		require.NoError(t, err)
		_ = deleteResp.Body.Close()

		assert.Equal(t, http.StatusNoContent, deleteResp.StatusCode)
	})

	t.Run("User cannot access another user's filter", func(t *testing.T) {
		// Create regular user
		user := createRegularUserOnce(t, setup.DB)
		userToken := loginUserWithCredentials(t, setup.Server, user.Email, user.Password)

		// User creates a filter
		createReq := map[string]interface{}{
			"name":          "User1 Private",
			"resource_type": "media-items",
			"filters":       map[string]string{},
		}
		body, _ := json.Marshal(createReq)

		createHTTP, _ := http.NewRequest("POST", setup.Server.URL+"/api/saved-filters", bytes.NewBuffer(body))
		createHTTP.Header.Set("Content-Type", "application/json")
		createHTTP.Header.Set("Authorization", "Bearer "+userToken)

		createResp, err := client.Do(createHTTP)
		require.NoError(t, err)
		defer func(Body io.ReadCloser) {
			_ = Body.Close()
		}(createResp.Body)

		var createdFilter map[string]interface{}
		err = json.NewDecoder(createResp.Body).Decode(&createdFilter)
		require.NoError(t, err)
		filterID := createdFilter["id"].(string)

		// Admin user tries to delete regular user's filter
		deleteHTTP, _ := http.NewRequest("DELETE", setup.Server.URL+"/api/saved-filters/"+filterID, nil)
		deleteHTTP.Header.Set("Authorization", "Bearer "+setup.Token)

		deleteResp, err := client.Do(deleteHTTP)
		require.NoError(t, err)
		_ = deleteResp.Body.Close()

		assert.Equal(t, http.StatusNotFound, deleteResp.StatusCode)
	})

	t.Run("GET /api/saved-filters/:id retrieves single filter", func(t *testing.T) {
		// First create a filter
		reqBody := map[string]interface{}{
			"name":          "Test Filter",
			"resource_type": "media-items",
			"filters":       map[string]string{"search": "test"},
		}
		body, _ := json.Marshal(reqBody)

		createReq, _ := http.NewRequest("POST", setup.Server.URL+"/api/saved-filters", bytes.NewBuffer(body))
		createReq.Header.Set("Content-Type", "application/json")
		createReq.Header.Set("Authorization", "Bearer "+setup.Token)

		createResp, err := client.Do(createReq)
		require.NoError(t, err)
		defer func(Body io.ReadCloser) {
			_ = Body.Close()
		}(createResp.Body)

		assert.Equal(t, http.StatusCreated, createResp.StatusCode)

		var createdFilter map[string]interface{}
		err = json.NewDecoder(createResp.Body).Decode(&createdFilter)
		require.NoError(t, err)
		filterID := createdFilter["id"].(string)

		// Now retrieve the filter by ID
		getReq, _ := http.NewRequest("GET", setup.Server.URL+"/api/saved-filters/"+filterID, nil)
		getReq.Header.Set("Authorization", "Bearer "+setup.Token)

		getResp, err := client.Do(getReq)
		require.NoError(t, err)
		defer func(Body io.ReadCloser) {
			_ = Body.Close()
		}(getResp.Body)

		assert.Equal(t, http.StatusOK, getResp.StatusCode)

		var retrievedFilter map[string]interface{}
		err = json.NewDecoder(getResp.Body).Decode(&retrievedFilter)
		require.NoError(t, err)
		assert.Equal(t, "Test Filter", retrievedFilter["name"])
		assert.Equal(t, "media-items", retrievedFilter["resource_type"])
		assert.Equal(t, filterID, retrievedFilter["id"])
		assert.NotEmpty(t, retrievedFilter["filters"])
	})

	t.Run("GET /api/saved-filters/:id without auth returns 401", func(t *testing.T) {
		httpReq, _ := http.NewRequest("GET", setup.Server.URL+"/api/saved-filters/550e8400-e29b-41d4-a716-446655440000", nil)
		resp, err := client.Do(httpReq)
		require.NoError(t, err)
		defer func(Body io.ReadCloser) {
			_ = Body.Close()
		}(resp.Body)

		assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
	})

	t.Run("GET /api/saved-filters/:id with invalid UUID returns 400", func(t *testing.T) {
		httpReq, _ := http.NewRequest("GET", setup.Server.URL+"/api/saved-filters/invalid-uuid", nil)
		httpReq.Header.Set("Authorization", "Bearer "+setup.Token)

		resp, err := client.Do(httpReq)
		require.NoError(t, err)
		defer func(Body io.ReadCloser) {
			_ = Body.Close()
		}(resp.Body)

		assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
	})

	t.Run("GET /api/saved-filters/:id with non-existent filter returns 404", func(t *testing.T) {
		fakeID := uuid.New().String()
		httpReq, _ := http.NewRequest("GET", setup.Server.URL+"/api/saved-filters/"+fakeID, nil)
		httpReq.Header.Set("Authorization", "Bearer "+setup.Token)

		resp, err := client.Do(httpReq)
		require.NoError(t, err)
		defer func(Body io.ReadCloser) {
			_ = Body.Close()
		}(resp.Body)

		assert.Equal(t, http.StatusNotFound, resp.StatusCode)
	})

	t.Run("GET /api/saved-filters/:id from different user returns 404", func(t *testing.T) {
		// Create filter with admin user (setup.Token)
		reqBody := map[string]interface{}{
			"name":          "Admin Filter",
			"resource_type": "media-items",
			"filters":       map[string]string{"search": "test"},
		}
		body, _ := json.Marshal(reqBody)
		createReq, _ := http.NewRequest("POST", setup.Server.URL+"/api/saved-filters", bytes.NewBuffer(body))
		createReq.Header.Set("Content-Type", "application/json")
		createReq.Header.Set("Authorization", "Bearer "+setup.Token)
		createResp, err := client.Do(createReq)
		require.NoError(t, err)
		defer func(Body io.ReadCloser) {
			_ = Body.Close()
		}(createResp.Body)
		var createdFilter map[string]interface{}
		err = json.NewDecoder(createResp.Body).Decode(&createdFilter)
		require.NoError(t, err)
		filterID := createdFilter["id"].(string)
		// Try to access with regular user (setup.RegularToken)
		httpReq, _ := http.NewRequest("GET", setup.Server.URL+"/api/saved-filters/"+filterID, nil)
		httpReq.Header.Set("Authorization", "Bearer "+setup.RegularToken)
		getResp, err := client.Do(httpReq)
		require.NoError(t, err)
		defer func(Body io.ReadCloser) {
			_ = Body.Close()
		}(getResp.Body)
		// Should return 404 (not 403 - hide existence)
		assert.Equal(t, http.StatusNotFound, getResp.StatusCode)
	})
}
