package main

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

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

// TestScannerEndpoints tests ebook scanner operations
func TestScannerEndpoints(t *testing.T) {
	t.Run("POST /api/scanner/scan - Scan without admin role", func(t *testing.T) {
		payload := map[string]interface{}{
			"folder_paths": []string{"/path/to/ebooks"},
		}
		jsonData, _ := json.Marshal(payload)

		req := httptest.NewRequest("POST", "/api/scanner/scan", bytes.NewBuffer(jsonData))
		req.Header.Set("Content-Type", "application/json")
		req.Header.Set("Authorization", "Bearer user-token")
		req.Header.Set("X-User-Role", "user")
		rr := httptest.NewRecorder()

		handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			userRole := r.Header.Get("X-User-Role")
			if userRole != "admin" {
				w.WriteHeader(http.StatusForbidden)
				w.Write([]byte(`{"error":"admin access required"}`))
				return
			}

			w.WriteHeader(http.StatusOK)
		})

		handler.ServeHTTP(rr, req)
		assert.Equal(t, http.StatusForbidden, rr.Code)
	})

	t.Run("POST /api/scanner/watch/start - Start watch mode", func(t *testing.T) {
		libraryID := uuid.New().String()

		payload := map[string]interface{}{
			"library_id": libraryID,
		}
		jsonData, _ := json.Marshal(payload)

		req := httptest.NewRequest("POST", "/api/scanner/watch/start", bytes.NewBuffer(jsonData))
		req.Header.Set("Content-Type", "application/json")
		req.Header.Set("Authorization", "Bearer admin-token")
		req.Header.Set("X-User-Role", "admin")
		rr := httptest.NewRecorder()

		handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.WriteHeader(http.StatusOK)
			response := map[string]interface{}{
				"message":    "watch mode started for library",
				"library_id": libraryID,
			}
			json.NewEncoder(w).Encode(response)
		})

		handler.ServeHTTP(rr, req)
		assert.Equal(t, http.StatusOK, rr.Code)

		var response map[string]interface{}
		json.Unmarshal(rr.Body.Bytes(), &response)
		assert.Equal(t, "watch mode started for library", response["message"])
		assert.Equal(t, libraryID, response["library_id"])
	})

	t.Run("POST /api/scanner/watch/stop - Stop watch mode", func(t *testing.T) {
		libraryID := uuid.New().String()

		payload := map[string]interface{}{
			"library_id": libraryID,
		}
		jsonData, _ := json.Marshal(payload)

		req := httptest.NewRequest("POST", "/api/scanner/watch/stop", bytes.NewBuffer(jsonData))
		req.Header.Set("Content-Type", "application/json")
		req.Header.Set("Authorization", "Bearer admin-token")
		req.Header.Set("X-User-Role", "admin")
		rr := httptest.NewRecorder()

		handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.WriteHeader(http.StatusOK)
			response := map[string]interface{}{
				"message":    "watch mode stopped for library",
				"library_id": libraryID,
			}
			json.NewEncoder(w).Encode(response)
		})

		handler.ServeHTTP(rr, req)
		assert.Equal(t, http.StatusOK, rr.Code)

		var response map[string]interface{}
		json.Unmarshal(rr.Body.Bytes(), &response)
		assert.Equal(t, "watch mode stopped for library", response["message"])
		assert.Equal(t, libraryID, response["library_id"])
	})

	t.Run("GET /api/scanner/watch/status - Get watch mode status", func(t *testing.T) {
		req := httptest.NewRequest("GET", "/api/scanner/watch/status", nil)
		req.Header.Set("Authorization", "Bearer admin-token")
		req.Header.Set("X-User-Role", "admin")
		rr := httptest.NewRecorder()

		handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.WriteHeader(http.StatusOK)
			response := map[string]interface{}{
				"watching_libraries": []string{
					uuid.New().String(),
					uuid.New().String(),
				},
				"total_watching": 2,
			}
			json.NewEncoder(w).Encode(response)
		})

		handler.ServeHTTP(rr, req)
		assert.Equal(t, http.StatusOK, rr.Code)

		var response map[string]interface{}
		json.Unmarshal(rr.Body.Bytes(), &response)
		assert.Equal(t, float64(2), response["total_watching"])
		assert.NotEmpty(t, response["watching_libraries"])
	})

	t.Run("GET /api/scanner/status/:jobId - Get job status", func(t *testing.T) {
		jobID := uuid.New().String()

		req := httptest.NewRequest("GET", "/api/scanner/status/"+jobID, nil)
		req.Header.Set("Authorization", "Bearer admin-token")
		req.Header.Set("X-User-Role", "admin")
		rr := httptest.NewRecorder()

		handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.WriteHeader(http.StatusOK)
			response := map[string]interface{}{
				"job_id":   jobID,
				"status":   "completed",
				"error":    "",
				"result":   map[string]string{"message": "scan completed"},
				"progress": 1.0,
			}
			json.NewEncoder(w).Encode(response)
		})

		handler.ServeHTTP(rr, req)
		assert.Equal(t, http.StatusOK, rr.Code)

		var response map[string]interface{}
		json.Unmarshal(rr.Body.Bytes(), &response)
		assert.Equal(t, jobID, response["job_id"])
		assert.Equal(t, "completed", response["status"])
	})

	t.Run("GET /api/scanner/status/:jobId - Job not found", func(t *testing.T) {
		jobID := uuid.New().String()

		req := httptest.NewRequest("GET", "/api/scanner/status/"+jobID, nil)
		req.Header.Set("Authorization", "Bearer admin-token")
		req.Header.Set("X-User-Role", "admin")
		rr := httptest.NewRecorder()

		handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.WriteHeader(http.StatusNotFound)
			w.Write([]byte(`{"error":"job not found"}`))
		})

		handler.ServeHTTP(rr, req)
		assert.Equal(t, http.StatusNotFound, rr.Code)
	})

	t.Run("POST /api/scanner/scan - Successful scan (background job)", func(t *testing.T) {
		payload := map[string]interface{}{
			"folder_paths": []string{"/valid/path"},
		}
		jsonData, _ := json.Marshal(payload)

		req := httptest.NewRequest("POST", "/api/scanner/scan", bytes.NewBuffer(jsonData))
		req.Header.Set("Content-Type", "application/json")
		req.Header.Set("Authorization", "Bearer admin-token")
		req.Header.Set("X-User-Role", "admin")
		rr := httptest.NewRecorder()

		handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.WriteHeader(http.StatusAccepted)
			response := map[string]interface{}{
				"message": "scan job enqueued",
				"job_id":  uuid.New().String(),
				"status":  "pending",
			}
			json.NewEncoder(w).Encode(response)
		})

		handler.ServeHTTP(rr, req)
		assert.Equal(t, http.StatusAccepted, rr.Code)

		var response map[string]interface{}
		json.Unmarshal(rr.Body.Bytes(), &response)
		assert.Equal(t, "scan job enqueued", response["message"])
		assert.NotEmpty(t, response["job_id"])
		assert.Equal(t, "pending", response["status"])
	})

	t.Run("POST /api/scanner/start - Start scanner without admin role", func(t *testing.T) {
		req := httptest.NewRequest("POST", "/api/scanner/start", nil)
		req.Header.Set("Authorization", "Bearer user-token")
		req.Header.Set("X-User-Role", "user")
		rr := httptest.NewRecorder()

		handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			userRole := r.Header.Get("X-User-Role")
			if userRole != "admin" {
				w.WriteHeader(http.StatusForbidden)
				w.Write([]byte(`{"error":"admin access required"}`))
				return
			}

			w.WriteHeader(http.StatusOK)
		})

		handler.ServeHTTP(rr, req)
		assert.Equal(t, http.StatusForbidden, rr.Code)
	})

	t.Run("POST /api/scanner/start - Start scanner successfully", func(t *testing.T) {
		req := httptest.NewRequest("POST", "/api/scanner/start", nil)
		req.Header.Set("Authorization", "Bearer admin-token")
		req.Header.Set("X-User-Role", "admin")
		rr := httptest.NewRecorder()

		handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.WriteHeader(http.StatusOK)
			w.Write([]byte(`{"message":"scanner started"}`))
		})

		handler.ServeHTTP(rr, req)
		assert.Equal(t, http.StatusOK, rr.Code)
	})

	t.Run("POST /api/scanner/stop - Stop scanner without admin role", func(t *testing.T) {
		req := httptest.NewRequest("POST", "/api/scanner/stop", nil)
		req.Header.Set("Authorization", "Bearer user-token")
		req.Header.Set("X-User-Role", "user")
		rr := httptest.NewRecorder()

		handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			userRole := r.Header.Get("X-User-Role")
			if userRole != "admin" {
				w.WriteHeader(http.StatusForbidden)
				w.Write([]byte(`{"error":"admin access required"}`))
				return
			}

			w.WriteHeader(http.StatusOK)
		})

		handler.ServeHTTP(rr, req)
		assert.Equal(t, http.StatusForbidden, rr.Code)
	})

	t.Run("POST /api/scanner/stop - Stop scanner successfully", func(t *testing.T) {
		req := httptest.NewRequest("POST", "/api/scanner/stop", nil)
		req.Header.Set("Authorization", "Bearer admin-token")
		req.Header.Set("X-User-Role", "admin")
		rr := httptest.NewRecorder()

		handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.WriteHeader(http.StatusOK)
			w.Write([]byte(`{"message":"scanner stopped"}`))
		})

		handler.ServeHTTP(rr, req)
		assert.Equal(t, http.StatusOK, rr.Code)
	})
}

// TestEdgeCases tests various edge cases and boundary conditions
func TestEdgeCases(t *testing.T) {
	t.Run("Empty request body", func(t *testing.T) {
		req := httptest.NewRequest("POST", "/api/auth/login", bytes.NewBuffer([]byte("")))
		req.Header.Set("Content-Type", "application/json")
		rr := httptest.NewRecorder()

		handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			var req map[string]interface{}
			if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
				w.WriteHeader(http.StatusBadRequest)
				w.Write([]byte(`{"error":"invalid request"}`))
				return
			}

			w.WriteHeader(http.StatusOK)
		})

		handler.ServeHTTP(rr, req)
		assert.Equal(t, http.StatusBadRequest, rr.Code)
	})

	t.Run("Malformed JSON", func(t *testing.T) {
		req := httptest.NewRequest("POST", "/api/auth/login", bytes.NewBuffer([]byte("{invalid json}")))
		req.Header.Set("Content-Type", "application/json")
		rr := httptest.NewRecorder()

		handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			var req map[string]interface{}
			if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
				w.WriteHeader(http.StatusBadRequest)
				w.Write([]byte(`{"error":"invalid request"}`))
				return
			}

			w.WriteHeader(http.StatusOK)
		})

		handler.ServeHTTP(rr, req)
		assert.Equal(t, http.StatusBadRequest, rr.Code)
	})

	t.Run("Very large payload", func(t *testing.T) {
		largeString := string(make([]byte, 100000))
		payload := map[string]interface{}{
			"email":    largeString + "@example.com",
			"username": "user",
			"password": "password123",
		}
		jsonData, _ := json.Marshal(payload)

		req := httptest.NewRequest("POST", "/api/auth/register", bytes.NewBuffer(jsonData))
		req.Header.Set("Content-Type", "application/json")
		rr := httptest.NewRecorder()

		handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.WriteHeader(http.StatusBadRequest)
			w.Write([]byte(`{"error":"email too long"}`))
		})

		handler.ServeHTTP(rr, req)
		assert.Equal(t, http.StatusBadRequest, rr.Code)
	})

	t.Run("SQL Injection attempt", func(t *testing.T) {
		payload := map[string]interface{}{
			"email":    "test@example.com'; DROP TABLE users; --",
			"username": "sqlinjection",
			"password": "password123",
		}
		jsonData, _ := json.Marshal(payload)

		req := httptest.NewRequest("POST", "/api/auth/register", bytes.NewBuffer(jsonData))
		req.Header.Set("Content-Type", "application/json")
		rr := httptest.NewRecorder()

		handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			var req map[string]interface{}
			if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
				w.WriteHeader(http.StatusBadRequest)
				w.Write([]byte(`{"error":"invalid request"}`))
				return
			}

			email := req["email"].(string)
			if len(email) > 255 {
				w.WriteHeader(http.StatusBadRequest)
				w.Write([]byte(`{"error":"email too long"}`))
				return
			}

			w.WriteHeader(http.StatusCreated)
		})

		handler.ServeHTTP(rr, req)
		// Should either succeed (if sanitized) or fail with validation error
		assert.True(t, rr.Code == http.StatusCreated || rr.Code == http.StatusBadRequest)
	})

	t.Run("XSS attempt in fields", func(t *testing.T) {
		payload := map[string]interface{}{
			"email":    "test@example.com",
			"username": "<script>alert('xss')</script>",
			"password": "password123",
		}
		jsonData, _ := json.Marshal(payload)

		req := httptest.NewRequest("POST", "/api/auth/register", bytes.NewBuffer(jsonData))
		req.Header.Set("Content-Type", "application/json")
		rr := httptest.NewRecorder()

		handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			var req map[string]interface{}
			if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
				w.WriteHeader(http.StatusBadRequest)
				w.Write([]byte(`{"error":"invalid request"}`))
				return
			}

			username := req["username"].(string)
			if len(username) > 50 {
				w.WriteHeader(http.StatusBadRequest)
				w.Write([]byte(`{"error":"username too long"}`))
				return
			}

			w.WriteHeader(http.StatusCreated)
		})

		handler.ServeHTTP(rr, req)
		assert.Equal(t, http.StatusCreated, rr.Code)
	})

	t.Run("Rate limiting simulation", func(t *testing.T) {
		requestCount := 0
		handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			requestCount++
			if requestCount > 10 {
				w.WriteHeader(http.StatusTooManyRequests)
				w.Write([]byte(`{"error":"too many requests"}`))
				return
			}

			w.WriteHeader(http.StatusOK)
		})

		for i := 0; i < 15; i++ {
			req := httptest.NewRequest("GET", "/api/libraries/types", nil)
			rr := httptest.NewRecorder()
			handler.ServeHTTP(rr, req)

			if i >= 10 {
				assert.Equal(t, http.StatusTooManyRequests, rr.Code)
			} else {
				assert.Equal(t, http.StatusOK, rr.Code)
			}
		}
	})
}

// TestHTMXRequests tests HTMX-specific responses
func TestHTMXRequests(t *testing.T) {
	t.Run("Registration with HTMX header", func(t *testing.T) {
		payload := map[string]interface{}{
			"email":    "htmx@example.com",
			"username": "htmxuser",
			"password": "password123",
		}
		jsonData, _ := json.Marshal(payload)

		req := httptest.NewRequest("POST", "/api/auth/register", bytes.NewBuffer(jsonData))
		req.Header.Set("Content-Type", "application/json")
		req.Header.Set("HX-Request", "true")
		rr := httptest.NewRecorder()

		handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			isHTMX := r.Header.Get("HX-Request") == "true"

			w.WriteHeader(http.StatusCreated)
			if isHTMX {
				w.Write([]byte(`<div class="text-green-500">Registration successful! Redirecting...</div>
<script>
localStorage.setItem('token', 'fake-token');
window.location.href = '/bookshelf';
</script>`))
			} else {
				json.NewEncoder(w).Encode(map[string]interface{}{
					"token": "fake-token",
					"user": map[string]interface{}{
						"id":       uuid.New().String(),
						"email":    "htmx@example.com",
						"username": "htmxuser",
					},
				})
			}
		})

		handler.ServeHTTP(rr, req)
		assert.Equal(t, http.StatusCreated, rr.Code)
		assert.Contains(t, rr.Body.String(), "<script>")
	})

	t.Run("Registration error with HTMX header", func(t *testing.T) {
		payload := map[string]interface{}{
			"email":    "existing@example.com",
			"username": "newuser",
			"password": "password123",
		}
		jsonData, _ := json.Marshal(payload)

		req := httptest.NewRequest("POST", "/api/auth/register", bytes.NewBuffer(jsonData))
		req.Header.Set("Content-Type", "application/json")
		req.Header.Set("HX-Request", "true")
		rr := httptest.NewRecorder()

		handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.WriteHeader(http.StatusConflict)
			w.Write([]byte(`<div class="text-red-500">Email already exists</div>`))
		})

		handler.ServeHTTP(rr, req)
		assert.Equal(t, http.StatusConflict, rr.Code)
		assert.Contains(t, rr.Body.String(), "text-red-500")
	})
}

// TestConcurrentRequests tests concurrent request handling
func TestConcurrentRequests(t *testing.T) {
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Write([]byte(`{"message":"ok"}`))
	})

	t.Run("Multiple concurrent requests", func(t *testing.T) {
		for i := 0; i < 10; i++ {
			req := httptest.NewRequest("GET", "/api/libraries/types", nil)
			rr := httptest.NewRecorder()
			handler.ServeHTTP(rr, req)
			assert.Equal(t, http.StatusOK, rr.Code)
		}
	})
}

// TestJWTValidation tests various JWT scenarios
func TestJWTValidation(t *testing.T) {
	t.Run("Valid JWT format", func(t *testing.T) {
		req := httptest.NewRequest("GET", "/api/libraries/types", nil)
		req.Header.Set("Authorization", "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.test")
		rr := httptest.NewRecorder()

		handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			authHeader := r.Header.Get("Authorization")
			if !containsPrefix(authHeader, "Bearer ") {
				w.WriteHeader(http.StatusUnauthorized)
				return
			}

			w.WriteHeader(http.StatusOK)
		})

		handler.ServeHTTP(rr, req)
		assert.Equal(t, http.StatusOK, rr.Code)
	})

	t.Run("Invalid JWT - no Bearer prefix", func(t *testing.T) {
		req := httptest.NewRequest("GET", "/api/libraries/types", nil)
		req.Header.Set("Authorization", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.test")
		rr := httptest.NewRecorder()

		handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			authHeader := r.Header.Get("Authorization")
			if authHeader == "" || !containsPrefix(authHeader, "Bearer ") {
				w.WriteHeader(http.StatusUnauthorized)
				w.Write([]byte(`{"message":"missing or malformed jwt"}`))
				return
			}

			w.WriteHeader(http.StatusOK)
		})

		handler.ServeHTTP(rr, req)
		assert.Equal(t, http.StatusUnauthorized, rr.Code)
	})

	t.Run("Invalid JWT - malformed", func(t *testing.T) {
		req := httptest.NewRequest("GET", "/api/libraries/types", nil)
		req.Header.Set("Authorization", "Bearer invalid.token.here")
		rr := httptest.NewRecorder()

		handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.WriteHeader(http.StatusUnauthorized)
			w.Write([]byte(`{"message":"missing or malformed jwt"}`))
		})

		handler.ServeHTTP(rr, req)
		assert.Equal(t, http.StatusUnauthorized, rr.Code)
	})
}
