//go:build ignore

package main

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

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

const baseTestURL = "http://localhost:8765/api"

// End-to-end integration test for complete application setup flow
func TestFullApplicationSetup(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test in short mode")
	}

	// Cleanup: Try to delete test user if it exists from previous test runs
	t.Run("Cleanup_ExistingTestUser", func(t *testing.T) {
		// Try to login as the test user first
		loginReq := map[string]interface{}{
			"login":    "admin@bookhoard.test",
			"password": "TestPassword123!@#",
		}

		body, _ := json.Marshal(loginReq)
		resp, err := http.Post(baseTestURL+"/auth/login", "application/json", bytes.NewBuffer(body))
		if err != nil {
			t.Logf("Cleanup: No existing test user to delete (server not available)")
			return
		}
		defer func(Body io.ReadCloser) {
			_ = Body.Close()
		}(resp.Body)

		// If login succeeds, try to delete the user
		if resp.StatusCode == http.StatusOK {
			var result map[string]interface{}
			err = json.NewDecoder(resp.Body).Decode(&result)
			require.NoError(t, err)

			if token, ok := result["access_token"].(string); ok && token != "" {
				// Delete the user using the token
				req, _ := http.NewRequest("DELETE", baseTestURL+"/auth/account", bytes.NewBuffer([]byte{}))
				req.Header.Set("Authorization", "Bearer "+token)
				req.Header.Set("Content-Type", "application/json")

				client := &http.Client{}
				delResp, err := client.Do(req)
				if err == nil {
					defer func(Body io.ReadCloser) {
						_ = Body.Close()
					}(delResp.Body)
					if delResp.StatusCode == http.StatusNoContent {
						t.Logf("Cleanup: Deleted existing test user")
					} else {
						t.Logf("Cleanup: Could not delete existing test user (HTTP %d)", delResp.StatusCode)
					}
				}

				// Also try to delete any libraries created by this user
				req, _ = http.NewRequest("GET", baseTestURL+"/libraries", bytes.NewBuffer([]byte{}))
				req.Header.Set("Authorization", "Bearer "+token)

				listResp, err := client.Do(req)
				if err == nil {
					defer func(Body io.ReadCloser) {
						_ = Body.Close()
					}(listResp.Body)
					if listResp.StatusCode == http.StatusOK {
						var libsResult map[string]interface{}
						err = json.NewDecoder(listResp.Body).Decode(&libsResult)
						require.NoError(t, err)

						if data, ok := libsResult["data"].([]interface{}); ok {
							for _, lib := range data {
								if libMap, ok := lib.(map[string]interface{}); ok {
									if libID, ok := libMap["id"].(string); ok {
										// Delete the library
										req, _ = http.NewRequest("DELETE", baseTestURL+"/libraries/"+libID, bytes.NewBuffer([]byte{}))
										req.Header.Set("Authorization", "Bearer "+token)
										delLibResp, _ := client.Do(req)
										if delLibResp != nil {
											_ = delLibResp.Body.Close()
										}
									}
								}
							}
						}
					}
				}
			}
		}

		// Wait a bit for cleanup to complete
		time.Sleep(500 * time.Millisecond)
	})

	// Step 1: Create first user (should be admin)
	t.Run("Step1_CreateFirstUser", func(t *testing.T) {
		userReq := map[string]interface{}{
			"email":      "admin@bookhoard.test",
			"username":   "admin",
			"password":   "TestPassword123!@#",
			"first_name": "Admin",
			"last_name":  "User",
		}

		body, _ := json.Marshal(userReq)
		resp, err := http.Post(baseTestURL+"/auth/register", "application/json", bytes.NewBuffer(body))
		assert.NoError(t, err)
		defer func(Body io.ReadCloser) {
			_ = Body.Close()
		}(resp.Body)

		// Accept 201 (Created) or 409 (Conflict if already exists from previous incomplete test run)
		if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusConflict {
			t.Fatalf("Expected 201 or 409, got %d", resp.StatusCode)
		}

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

		// If we got 409, the user already exists, so we need to login to get the token
		if resp.StatusCode == http.StatusConflict {
			t.Logf("User already exists, logging in instead...")
			loginReq := map[string]interface{}{
				"login":    "admin@bookhoard.test",
				"password": "TestPassword123!@#",
			}
			body, _ := json.Marshal(loginReq)
			resp2, err := http.Post(baseTestURL+"/auth/login", "application/json", bytes.NewBuffer(body))
			assert.NoError(t, err)
			defer func(Body io.ReadCloser) {
				_ = Body.Close()
			}(resp2.Body)

			assert.Equal(t, http.StatusOK, resp2.StatusCode)
			err = json.NewDecoder(resp2.Body).Decode(&result)
			require.NoError(t, err)
		}

		if result["user"] != nil {
			user, ok := result["user"].(map[string]interface{})
			assert.True(t, ok, "User field should exist")
			assert.Equal(t, "admin", user["username"])
			assert.Equal(t, "admin", user["role"], "First user should be admin")
		}

		t.Logf("✅ Step 1 PASSED: First user created with admin role")
	})

	// Login as admin
	var adminToken string
	t.Run("LoginAsAdmin", func(t *testing.T) {
		loginReq := map[string]interface{}{
			"login":    "admin@bookhoard.test",
			"password": "TestPassword123!@#",
		}

		body, _ := json.Marshal(loginReq)
		resp, err := http.Post(baseTestURL+"/auth/login", "application/json", bytes.NewBuffer(body))
		assert.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)

		token, ok := result["access_token"].(string)
		assert.True(t, ok, "Should have access_token")
		adminToken = token
		assert.NotEmpty(t, adminToken)
	})

	// Step 2: Create first library with ebook type
	var libraryID string
	t.Run("Step2_CreateFirstLibrary", func(t *testing.T) {
		libraryReq := map[string]interface{}{
			"name":        "Test Library",
			"description": "Integration test library",
			"type":        "ebooks",
		}

		body, _ := json.Marshal(libraryReq)
		req, _ := http.NewRequest("POST", baseTestURL+"/libraries", bytes.NewBuffer(body))
		req.Header.Set("Authorization", "Bearer "+adminToken)
		req.Header.Set("Content-Type", "application/json")

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

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

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

		// Safe extraction of library ID with nil check
		if result["id"] == nil {
			t.Fatalf("Expected library ID in response, got nil")
		}

		var ok bool
		libraryID, ok = result["id"].(string)
		if !ok {
			t.Fatalf("Expected library ID to be string, got %T", result["id"])
		}

		assert.NotEmpty(t, libraryID)
		assert.Equal(t, "Test Library", result["name"])

		t.Logf("✅ Step 2 PASSED: First library created with ID: %s", libraryID)
	})

	// Step 3: Add /app/uploads folder to the library
	t.Run("Step3_AddUploadsFolder", func(t *testing.T) {
		folderReq := map[string]interface{}{
			"folder_path": getUploadPath(),
		}

		body, _ := json.Marshal(folderReq)
		url := fmt.Sprintf("%s/libraries/%s/folders", baseTestURL, libraryID)
		req, _ := http.NewRequest("POST", url, bytes.NewBuffer(body))
		req.Header.Set("Authorization", "Bearer "+adminToken)
		req.Header.Set("Content-Type", "application/json")

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

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

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

		assert.Equal(t, getUploadPath(), result["folder_path"])

		t.Logf("✅ Step 3 PASSED: %s folder added to library", getUploadPath())
	})

	// Step 4: Scan the library
	t.Run("Step4_ScanLibrary", func(t *testing.T) {
		scanReq := map[string]interface{}{
			"library_id": libraryID,
		}

		body, _ := json.Marshal(scanReq)
		req, _ := http.NewRequest("POST", baseTestURL+"/scanner/scan", bytes.NewBuffer(body))
		req.Header.Set("Authorization", "Bearer "+adminToken)
		req.Header.Set("Content-Type", "application/json")

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

		// Accept 200 or 202
		assert.Contains(t, []int{http.StatusOK, http.StatusAccepted}, resp.StatusCode)

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

		assert.Equal(t, "success", result["status"])

		t.Logf("✅ Step 4 PASSED: Library scan initiated")
	})

	// Wait for scan to complete
	time.Sleep(2 * time.Second)

	// Step 5: List media-items
	t.Run("Step5_ListMediaItems", func(t *testing.T) {
		url := fmt.Sprintf("%s/libraries/%s/media-items", baseTestURL, libraryID)
		req, _ := http.NewRequest("GET", url, nil)
		req.Header.Set("Authorization", "Bearer "+adminToken)

		client := &http.Client{}
		resp, err := client.Do(req)
		assert.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)

		data, ok := result["data"].([]interface{})
		assert.True(t, ok, "Data field should exist")
		assert.True(t, len(data) >= 0, "Should return data array")

		t.Logf("✅ Step 5 PASSED: Media items listed (count: %d)", len(data))
	})
}

// Helper function to read response body
func readBody(resp *http.Response) string {
	body, _ := io.ReadAll(resp.Body)
	return string(body)
}
