package server_test

import (
	"bytes"
	"encoding/json"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"testing"
	"time"

	"github.com/sirupsen/logrus"
	"markdown-editor-backend/server"
)

func TestServerCRUD(t *testing.T) {
	// Create temp directory for test data
	tempDir, err := os.MkdirTemp("", "markdown-test-*")
	if err != nil {
		t.Fatalf("Failed to create temp dir: %v", err)
	}
	defer os.RemoveAll(tempDir)

	// Create static directory
	staticDir := filepath.Join(tempDir, "static")
	if err := os.MkdirAll(staticDir, 0755); err != nil {
		t.Fatalf("Failed to create static dir: %v", err)
	}
	if err := os.WriteFile(filepath.Join(staticDir, "index.html"), []byte("<html></html>"), 0644); err != nil {
		t.Fatalf("Failed to create index.html: %v", err)
	}

	// Create logger
	log := logrus.New()
	log.SetLevel(logrus.WarnLevel)

	// Create server with dynamic port
	srv := server.New(tempDir, "127.0.0.1", 0, log)
	srv.SetStaticPath(staticDir)

	// Start server asynchronously
	addr, err := srv.StartAsync()
	if err != nil {
		t.Fatalf("Failed to start server: %v", err)
	}
	defer srv.Stop()

	// Wait for server to be ready
	time.Sleep(100 * time.Millisecond)

	baseURL := "http://" + addr
	client := &http.Client{Timeout: 5 * time.Second}

	// Test health endpoint
	t.Run("HealthCheck", func(t *testing.T) {
		resp, err := client.Get(baseURL + "/api/health")
		if err != nil {
			t.Fatalf("Health check failed: %v", err)
		}
		defer resp.Body.Close()

		if resp.StatusCode != http.StatusOK {
			t.Errorf("Expected status 200, got %d", resp.StatusCode)
		}

		var result map[string]string
		if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
			t.Fatalf("Failed to decode response: %v", err)
		}

		if result["status"] != "ok" {
			t.Errorf("Expected status 'ok', got '%s'", result["status"])
		}
	})

	// Test Create
	t.Run("CreateFile", func(t *testing.T) {
		body := map[string]string{
			"name":    "test-file",
			"content": "# Hello World\n\nThis is a test file.",
		}
		jsonBody, _ := json.Marshal(body)

		resp, err := client.Post(baseURL+"/api/files", "application/json", bytes.NewBuffer(jsonBody))
		if err != nil {
			t.Fatalf("Create file failed: %v", err)
		}
		defer resp.Body.Close()

		if resp.StatusCode != http.StatusCreated {
			t.Errorf("Expected status 201, got %d", resp.StatusCode)
		}

		var result map[string]string
		if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
			t.Fatalf("Failed to decode response: %v", err)
		}

		if result["name"] != "test-file" {
			t.Errorf("Expected name 'test-file', got '%s'", result["name"])
		}
	})

	// Test List
	t.Run("ListFiles", func(t *testing.T) {
		resp, err := client.Get(baseURL + "/api/files")
		if err != nil {
			t.Fatalf("List files failed: %v", err)
		}
		defer resp.Body.Close()

		if resp.StatusCode != http.StatusOK {
			t.Errorf("Expected status 200, got %d", resp.StatusCode)
		}

		var result []string
		if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
			t.Fatalf("Failed to decode response: %v", err)
		}

		found := false
		for _, name := range result {
			if name == "test-file" {
				found = true
				break
			}
		}

		if !found {
			t.Errorf("Expected 'test-file' in list, got %v", result)
		}
	})

	// Test Get
	t.Run("GetFile", func(t *testing.T) {
		resp, err := client.Get(baseURL + "/api/files/test-file")
		if err != nil {
			t.Fatalf("Get file failed: %v", err)
		}
		defer resp.Body.Close()

		if resp.StatusCode != http.StatusOK {
			t.Errorf("Expected status 200, got %d", resp.StatusCode)
		}

		var result map[string]string
		if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
			t.Fatalf("Failed to decode response: %v", err)
		}

		if result["name"] != "test-file" {
			t.Errorf("Expected name 'test-file', got '%s'", result["name"])
		}

		if result["content"] != "# Hello World\n\nThis is a test file." {
			t.Errorf("Expected specific content, got '%s'", result["content"])
		}
	})

	// Test Update
	t.Run("UpdateFile", func(t *testing.T) {
		body := map[string]string{
			"content": "# Updated Content\n\nThis file has been updated.",
		}
		jsonBody, _ := json.Marshal(body)

		req, _ := http.NewRequest(http.MethodPut, baseURL+"/api/files/test-file", bytes.NewBuffer(jsonBody))
		req.Header.Set("Content-Type", "application/json")

		resp, err := client.Do(req)
		if err != nil {
			t.Fatalf("Update file failed: %v", err)
		}
		defer resp.Body.Close()

		if resp.StatusCode != http.StatusOK {
			t.Errorf("Expected status 200, got %d", resp.StatusCode)
		}

		// Verify the update
		resp2, _ := client.Get(baseURL + "/api/files/test-file")
		defer resp2.Body.Close()

		var result map[string]string
		json.NewDecoder(resp2.Body).Decode(&result)

		if result["content"] != "# Updated Content\n\nThis file has been updated." {
			t.Errorf("Expected updated content, got '%s'", result["content"])
		}
	})

	// Test Delete
	t.Run("DeleteFile", func(t *testing.T) {
		req, _ := http.NewRequest(http.MethodDelete, baseURL+"/api/files/test-file", nil)

		resp, err := client.Do(req)
		if err != nil {
			t.Fatalf("Delete file failed: %v", err)
		}
		defer resp.Body.Close()

		if resp.StatusCode != http.StatusNoContent {
			t.Errorf("Expected status 204, got %d", resp.StatusCode)
		}

		// Verify deletion
		resp2, _ := client.Get(baseURL + "/api/files/test-file")
		defer resp2.Body.Close()

		if resp2.StatusCode != http.StatusNotFound {
			t.Errorf("Expected status 404 for deleted file, got %d", resp2.StatusCode)
		}
	})

	// Test 404
	t.Run("GetNonExistentFile", func(t *testing.T) {
		resp, err := client.Get(baseURL + "/api/files/nonexistent")
		if err != nil {
			t.Fatalf("Request failed: %v", err)
		}
		defer resp.Body.Close()

		if resp.StatusCode != http.StatusNotFound {
			t.Errorf("Expected status 404, got %d", resp.StatusCode)
		}

		var result map[string]string
		if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
			t.Fatalf("Failed to decode response: %v", err)
		}

		if result["error"] != "file not found" {
			t.Errorf("Expected error 'file not found', got '%s'", result["error"])
		}
	})

	// Test static file serving
	t.Run("StaticFiles", func(t *testing.T) {
		resp, err := client.Get(baseURL + "/")
		if err != nil {
			t.Fatalf("Get static file failed: %v", err)
		}
		defer resp.Body.Close()

		if resp.StatusCode != http.StatusOK {
			t.Errorf("Expected status 200, got %d", resp.StatusCode)
		}

		body, _ := io.ReadAll(resp.Body)
		if string(body) != "<html></html>" {
			t.Errorf("Expected '<html></html>', got '%s'", string(body))
		}
	})
}
