package main

import (
	"bookhoard/internal/handlers"
	"bytes"
	"encoding/json"
	"io"
	"net/http"
	"testing"
	"time"

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

// TestAnalyticsReadingStats tests the reading statistics endpoint
func TestAnalyticsReadingStats(t *testing.T) {
	setup := setupTestServer(t)
	client := &http.Client{}

	t.Run("GetReadingStats_WithoutAuth", func(t *testing.T) {
		req, _ := http.NewRequest("GET", setup.Server.URL+"/api/analytics/reading-stats", nil)
		resp, err := client.Do(req)
		require.NoError(t, err)
		defer func(Body io.ReadCloser) {
			_ = Body.Close()
		}(resp.Body)

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

	t.Run("GetReadingStats_WithAuth_DefaultDates", func(t *testing.T) {
		req, _ := http.NewRequest("GET", setup.Server.URL+"/api/analytics/reading-stats", nil)
		req.Header.Set("Authorization", "Bearer "+setup.Token)

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

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

		var result handlers.ReadingStatsResponse
		err = json.NewDecoder(resp.Body).Decode(&result)
		require.NoError(t, err)

		assert.GreaterOrEqual(t, result.TotalBooksRead, 0)
		assert.GreaterOrEqual(t, result.TotalPagesRead, 0)
		assert.GreaterOrEqual(t, result.TotalReadingTime, 0)
	})

	t.Run("GetReadingStats_WithCustomDateRange", func(t *testing.T) {
		startDate := time.Now().AddDate(0, -2, 0).Format("01-02-2006")
		endDate := time.Now().Format("01-02-2006")

		req, _ := http.NewRequest("GET", setup.Server.URL+"/api/analytics/reading-stats?start_date="+startDate+"&end_date="+endDate, nil)
		req.Header.Set("Authorization", "Bearer "+setup.Token)

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

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

	t.Run("GetReadingStats_InvalidStartDate", func(t *testing.T) {
		req, _ := http.NewRequest("GET", setup.Server.URL+"/api/analytics/reading-stats?start_date=invalid-date", nil)
		req.Header.Set("Authorization", "Bearer "+setup.Token)

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

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

	t.Run("GetReadingStats_InvalidEndDate", func(t *testing.T) {
		req, _ := http.NewRequest("GET", setup.Server.URL+"/api/analytics/reading-stats?end_date=not-a-date", nil)
		req.Header.Set("Authorization", "Bearer "+setup.Token)

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

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

	t.Run("GetReadingStats_EmptyHistory", func(t *testing.T) {
		req, _ := http.NewRequest("GET", setup.Server.URL+"/api/analytics/reading-stats", nil)
		req.Header.Set("Authorization", "Bearer "+setup.Token)

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

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

		var result map[string]interface{}
		err = json.NewDecoder(resp.Body).Decode(&result)
		require.NoError(t, err)

		// Should return zero values for empty history
		assert.Equal(t, 0.0, result["total_books_read"])
		assert.Equal(t, 0.0, result["total_pages_read"])
	})
}

// TestAnalyticsDeviceUsage tests the device usage endpoint
func TestAnalyticsDeviceUsage(t *testing.T) {
	setup := setupTestServer(t)
	client := &http.Client{}

	t.Run("GetDeviceUsage_WithoutAuth", func(t *testing.T) {
		req, _ := http.NewRequest("GET", setup.Server.URL+"/api/analytics/device-usage", nil)
		resp, err := client.Do(req)
		require.NoError(t, err)
		defer func(Body io.ReadCloser) {
			_ = Body.Close()
		}(resp.Body)

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

	t.Run("GetDeviceUsage_WithAuth_NoDevices", func(t *testing.T) {
		req, _ := http.NewRequest("GET", setup.Server.URL+"/api/analytics/device-usage", nil)
		req.Header.Set("Authorization", "Bearer "+setup.Token)

		resp, err := client.Do(req)
		require.NoError(t, err)

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

		var result handlers.DeviceUsageResponse
		err = json.NewDecoder(resp.Body).Decode(&result)
		require.NoError(t, err)

		assert.NotNil(t, result.Devices)
		assert.Equal(t, 0, len(result.Devices))
	})

	t.Run("GetDeviceUsage_WithAuth_WithDevices", func(t *testing.T) {
		// First, create a device
		deviceReq := map[string]interface{}{
			"device_name": "Test Kobo",
			"device_type": "kobo",
		}
		deviceBody, _ := json.Marshal(deviceReq)

		deviceReqHTTP, _ := http.NewRequest("POST", setup.Server.URL+"/api/devices/register", bytes.NewBuffer(deviceBody))
		deviceReqHTTP.Header.Set("Content-Type", "application/json")
		deviceReqHTTP.Header.Set("Authorization", "Bearer "+setup.Token)

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

		// Now get device usage
		req, _ := http.NewRequest("GET", setup.Server.URL+"/api/analytics/device-usage", nil)
		req.Header.Set("Authorization", "Bearer "+setup.Token)

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

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

		var result map[string]interface{}
		err = json.NewDecoder(resp.Body).Decode(&result)
		require.NoError(t, err)

		devices, ok := result["devices"].([]interface{})
		assert.True(t, ok)
		// May be 0 if device has no usage data yet
		assert.NotNil(t, devices)
	})

	t.Run("GetDeviceUsage_ResponseStructure", func(t *testing.T) {
		req, _ := http.NewRequest("GET", setup.Server.URL+"/api/analytics/device-usage", nil)
		req.Header.Set("Authorization", "Bearer "+setup.Token)

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

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

		var result map[string]interface{}
		err = json.NewDecoder(resp.Body).Decode(&result)
		require.NoError(t, err)

		assert.Contains(t, result, "devices")

		// Check device structure if any devices exist
		if devices, ok := result["devices"].([]interface{}); ok && len(devices) > 0 {
			firstDevice := devices[0].(map[string]interface{})
			assert.Contains(t, firstDevice, "device_id")
			assert.Contains(t, firstDevice, "device_name")
			assert.Contains(t, firstDevice, "device_type")
			assert.Contains(t, firstDevice, "sync_count")
			assert.Contains(t, firstDevice, "last_sync")
			assert.Contains(t, firstDevice, "total_time_seconds")
			assert.Contains(t, firstDevice, "total_time_minutes")
		}
	})
}

// TestAnalyticsPopularBooks tests the popular books endpoint
func TestAnalyticsPopularBooks(t *testing.T) {
	setup := setupTestServer(t)
	client := &http.Client{}

	t.Run("GetPopularBooks_WithoutAuth", func(t *testing.T) {
		req, _ := http.NewRequest("GET", setup.Server.URL+"/api/analytics/popular-books", nil)
		resp, err := client.Do(req)
		require.NoError(t, err)
		defer func(Body io.ReadCloser) {
			_ = Body.Close()
		}(resp.Body)

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

	t.Run("GetPopularBooks_WithAuth_DefaultLimit", func(t *testing.T) {
		req, _ := http.NewRequest("GET", setup.Server.URL+"/api/analytics/popular-books", nil)
		req.Header.Set("Authorization", "Bearer "+setup.Token)

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

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

		var result handlers.PopularBooksResponse
		err = json.NewDecoder(resp.Body).Decode(&result)
		require.NoError(t, err)

		assert.NotNil(t, result.Books)
		// Default limit is 10, but may be fewer if no reading history
		assert.True(t, len(result.Books) <= 10)
	})

	t.Run("GetPopularBooks_WithCustomLimit", func(t *testing.T) {
		req, _ := http.NewRequest("GET", setup.Server.URL+"/api/analytics/popular-books?limit=5", nil)
		req.Header.Set("Authorization", "Bearer "+setup.Token)

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

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

		var result map[string]interface{}
		err = json.NewDecoder(resp.Body).Decode(&result)
		require.NoError(t, err)

		books := result["books"].([]interface{})
		assert.True(t, len(books) <= 5)
	})

	t.Run("GetPopularBooks_InvalidLimit", func(t *testing.T) {
		req, _ := http.NewRequest("GET", setup.Server.URL+"/api/analytics/popular-books?limit=invalid", nil)
		req.Header.Set("Authorization", "Bearer "+setup.Token)

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

		// Should default to 10 on invalid limit
		assert.Equal(t, http.StatusOK, resp.StatusCode)

		var result map[string]interface{}
		err = json.NewDecoder(resp.Body).Decode(&result)
		require.NoError(t, err)

		books := result["books"].([]interface{})
		assert.True(t, len(books) <= 10)
	})

	t.Run("GetPopularBooks_ResponseStructure", func(t *testing.T) {
		// First, create a book and some reading history
		bookID := createTestMediaItemID(t, setup)

		// Create reading history for the book
		historyReq := map[string]interface{}{
			"media_item_id":       bookID,
			"progress_percentage": 50.0,
			"pages_read":          100,
			"time_spent_seconds":  1800,
		}
		historyBody, _ := json.Marshal(historyReq)

		historyHTTP, _ := http.NewRequest("POST", setup.Server.URL+"/api/media-items/"+bookID+"/progress", bytes.NewBuffer(historyBody))
		historyHTTP.Header.Set("Content-Type", "application/json")
		historyHTTP.Header.Set("Authorization", "Bearer "+setup.Token)

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

		// Now get popular books
		req, _ := http.NewRequest("GET", setup.Server.URL+"/api/analytics/popular-books", nil)
		req.Header.Set("Authorization", "Bearer "+setup.Token)

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

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

		var result map[string]interface{}
		err = json.NewDecoder(resp.Body).Decode(&result)
		require.NoError(t, err)

		books := result["books"].([]interface{})

		// Check book structure if any books exist
		if len(books) > 0 {
			firstBook := books[0].(map[string]interface{})
			assert.Contains(t, firstBook, "media_item_id")
			assert.Contains(t, firstBook, "title")
			assert.Contains(t, firstBook, "author")
			assert.Contains(t, firstBook, "read_count")
			assert.Contains(t, firstBook, "avg_completion")
			assert.Contains(t, firstBook, "last_read")
		}
	})

	t.Run("GetPopularBooks_NoReadingHistory", func(t *testing.T) {
		req, _ := http.NewRequest("GET", setup.Server.URL+"/api/analytics/popular-books", nil)
		req.Header.Set("Authorization", "Bearer "+setup.Token)

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

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

		var result map[string]interface{}
		err = json.NewDecoder(resp.Body).Decode(&result)
		require.NoError(t, err)

		books := result["books"].([]interface{})
		// Should return empty array if no reading history
		assert.Equal(t, 0, len(books))
	})
}

// TestAnalyticsEdgeCases tests edge cases for analytics endpoints
func TestAnalyticsEdgeCases(t *testing.T) {
	setup := setupTestServer(t)
	client := &http.Client{}

	t.Run("ReadingStats_FutureDateRange", func(t *testing.T) {
		startDate := time.Now().AddDate(0, 0, 7).Format("01-02-2006")
		endDate := time.Now().AddDate(0, 0, 14).Format("01-02-2006")

		req, _ := http.NewRequest("GET", setup.Server.URL+"/api/analytics/reading-stats?start_date="+startDate+"&end_date="+endDate, nil)
		req.Header.Set("Authorization", "Bearer "+setup.Token)

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

		// Should succeed but return empty stats
		assert.Equal(t, http.StatusOK, resp.StatusCode)

		var result map[string]interface{}
		err = json.NewDecoder(resp.Body).Decode(&result)
		require.NoError(t, err)

		assert.Equal(t, 0.0, result["total_books_read"])
	})

	t.Run("PopularBooks_LimitZero", func(t *testing.T) {
		req, _ := http.NewRequest("GET", setup.Server.URL+"/api/analytics/popular-books?limit=0", nil)
		req.Header.Set("Authorization", "Bearer "+setup.Token)

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

		// Should handle limit=0 gracefully
		assert.Equal(t, http.StatusOK, resp.StatusCode)

		var result map[string]interface{}
		err = json.NewDecoder(resp.Body).Decode(&result)
		require.NoError(t, err)

		books := result["books"].([]interface{})
		assert.Equal(t, 0, len(books))
	})

	t.Run("PopularBooks_VeryLargeLimit", func(t *testing.T) {
		req, _ := http.NewRequest("GET", setup.Server.URL+"/api/analytics/popular-books?limit=999999", nil)
		req.Header.Set("Authorization", "Bearer "+setup.Token)

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

		// Should handle large limit
		assert.Equal(t, http.StatusOK, resp.StatusCode)
	})
}
