// src/forgejo_test.go
package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"reflect"
	"strings"
	"testing"
)

func TestSyncLabels(t *testing.T) {
	// Mock server to simulate Forgejo API
	handler := http.NewServeMux()
	existingLabels := []ForgejoLabel{
		{ID: 1, Name: "area/forgejo", Color: "0e8a16", Description: "Old description."},
		{ID: 2, Name: "area/kubernetes", Color: "0e8a16", Description: "Issues pertaining to Kubernetes integration and deployment."},
		{ID: 3, Name: "obsolete/label", Color: "ff0000", Description: "Obsolete label to be deleted."},
	}

	handler.HandleFunc("/repos/owner/repo/labels", func(w http.ResponseWriter, r *http.Request) {
		switch r.Method {
		case "GET":
			json.NewEncoder(w).Encode(existingLabels)
		case "POST":
			var newLabel CreateLabelOption
			json.NewDecoder(r.Body).Decode(&newLabel)
			newLabelID := int64(len(existingLabels) + 1)
			createdLabel := ForgejoLabel{
				ID:          newLabelID,
				Name:        newLabel.Name,
				Color:       newLabel.Color,
				Description: newLabel.Description,
			}
			existingLabels = append(existingLabels, createdLabel)
			w.WriteHeader(http.StatusCreated)
			json.NewEncoder(w).Encode(createdLabel)
		case "DELETE":
			// Extract label ID from URL
			// URL format: /repos/owner/repo/labels/{id}
			parts := strings.Split(r.URL.Path, "/")
			if len(parts) < 6 {
				http.Error(w, "Invalid URL", http.StatusBadRequest)
				return
			}
			var id int64
			_, err := fmt.Sscanf(parts[5], "%d", &id)
			if err != nil {
				http.Error(w, "Invalid ID", http.StatusBadRequest)
				return
			}
			// Find and delete the label
			for i, label := range existingLabels {
				if label.ID == id {
					existingLabels = append(existingLabels[:i], existingLabels[i+1:]...)
					w.WriteHeader(http.StatusNoContent)
					return
				}
			}
			http.Error(w, "Label not found", http.StatusNotFound)
		default:
			http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		}
	})

	handler.HandleFunc("/repos/owner/repo/labels/", func(w http.ResponseWriter, r *http.Request) {
		// Handle PATCH requests for updating labels
		if r.Method != "PATCH" {
			http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
			return
		}

		// Extract label ID from URL
		// URL format: /repos/owner/repo/labels/{id}
		parts := strings.Split(r.URL.Path, "/")
		if len(parts) < 6 {
			http.Error(w, "Invalid URL", http.StatusBadRequest)
			return
		}
		var id int64
		_, err := fmt.Sscanf(parts[5], "%d", &id)
		if err != nil {
			http.Error(w, "Invalid ID", http.StatusBadRequest)
			return
		}

		// Find the label
		var labelToUpdate *ForgejoLabel
		for i := range existingLabels {
			if existingLabels[i].ID == id {
				labelToUpdate = &existingLabels[i]
				break
			}
		}
		if labelToUpdate == nil {
			http.Error(w, "Label not found", http.StatusNotFound)
			return
		}

		// Decode the update options
		var updateOption EditLabelOption
		json.NewDecoder(r.Body).Decode(&updateOption)

		// Apply updates
		if updateOption.Color != "" {
			labelToUpdate.Color = updateOption.Color
		}
		if updateOption.Description != "" {
			labelToUpdate.Description = updateOption.Description
		}

		// Respond with the updated label
		w.WriteHeader(http.StatusOK)
		json.NewEncoder(w).Encode(labelToUpdate)
	})

	server := httptest.NewServer(handler)
	defer server.Close()

	client := NewForgejoClient(server.URL, "")
	labelsConfig := []ForgejoLabel{
		{Name: "area/forgejo", Color: "0e8a16", Description: "Issues related to the Forgejo platform itself."},
		{Name: "area/kubernetes", Color: "0e8a16", Description: "Issues pertaining to Kubernetes integration and deployment."},
		{Name: "new/label", Color: "123456", Description: "A new label to be added."},
	}

	// Perform sync with deleteOtherLabels = true and dryRun = false
	err := EnsureLabelsExist(client, "owner", "repo", labelsConfig, true, false)
	if err != nil {
		t.Fatalf("EnsureLabelsExist returned an error: %v", err)
	}

	// Expected labels after sync
	expectedLabels := []ForgejoLabel{
		{ID: 1, Name: "area/forgejo", Color: "0e8a16", Description: "Issues related to the Forgejo platform itself."},
		{ID: 2, Name: "area/kubernetes", Color: "0e8a16", Description: "Issues pertaining to Kubernetes integration and deployment."},
		{ID: 4, Name: "new/label", Color: "123456", Description: "A new label to be added."},
	}

	// Verify the labels in the mock server
	if !reflect.DeepEqual(existingLabels, expectedLabels) {
		t.Errorf("Expected labels %v, got %v", expectedLabels, existingLabels)
	}
}

func TestSyncLabelsWithoutDeletion(t *testing.T) {
	// Mock server to simulate Forgejo API
	handler := http.NewServeMux()
	existingLabels := []ForgejoLabel{
		{ID: 1, Name: "area/forgejo", Color: "0e8a16", Description: "Old description."},
		{ID: 2, Name: "area/kubernetes", Color: "0e8a16", Description: "Issues pertaining to Kubernetes integration and deployment."},
		{ID: 3, Name: "obsolete/label", Color: "ff0000", Description: "Obsolete label to be deleted."},
	}

	handler.HandleFunc("/repos/owner/repo/labels", func(w http.ResponseWriter, r *http.Request) {
		switch r.Method {
		case "GET":
			json.NewEncoder(w).Encode(existingLabels)
		case "POST":
			var newLabel CreateLabelOption
			json.NewDecoder(r.Body).Decode(&newLabel)
			newLabelID := int64(len(existingLabels) + 1)
			createdLabel := ForgejoLabel{
				ID:          newLabelID,
				Name:        newLabel.Name,
				Color:       newLabel.Color,
				Description: newLabel.Description,
			}
			existingLabels = append(existingLabels, createdLabel)
			w.WriteHeader(http.StatusCreated)
			json.NewEncoder(w).Encode(createdLabel)
		case "DELETE":
			// Extract label ID from URL
			// URL format: /repos/owner/repo/labels/{id}
			parts := strings.Split(r.URL.Path, "/")
			if len(parts) < 6 {
				http.Error(w, "Invalid URL", http.StatusBadRequest)
				return
			}
			var id int64
			_, err := fmt.Sscanf(parts[5], "%d", &id)
			if err != nil {
				http.Error(w, "Invalid ID", http.StatusBadRequest)
				return
			}
			// Find and delete the label
			for i, label := range existingLabels {
				if label.ID == id {
					existingLabels = append(existingLabels[:i], existingLabels[i+1:]...)
					w.WriteHeader(http.StatusNoContent)
					return
				}
			}
			http.Error(w, "Label not found", http.StatusNotFound)
		default:
			http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		}
	})

	handler.HandleFunc("/repos/owner/repo/labels/", func(w http.ResponseWriter, r *http.Request) {
		// Handle PATCH requests for updating labels
		if r.Method != "PATCH" {
			http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
			return
		}

		// Extract label ID from URL
		// URL format: /repos/owner/repo/labels/{id}
		parts := strings.Split(r.URL.Path, "/")
		if len(parts) < 6 {
			http.Error(w, "Invalid URL", http.StatusBadRequest)
			return
		}
		var id int64
		_, err := fmt.Sscanf(parts[5], "%d", &id)
		if err != nil {
			http.Error(w, "Invalid ID", http.StatusBadRequest)
			return
		}

		// Find the label
		var labelToUpdate *ForgejoLabel
		for i := range existingLabels {
			if existingLabels[i].ID == id {
				labelToUpdate = &existingLabels[i]
				break
			}
		}
		if labelToUpdate == nil {
			http.Error(w, "Label not found", http.StatusNotFound)
			return
		}

		// Decode the update options
		var updateOption EditLabelOption
		json.NewDecoder(r.Body).Decode(&updateOption)

		// Apply updates
		if updateOption.Color != "" {
			labelToUpdate.Color = updateOption.Color
		}
		if updateOption.Description != "" {
			labelToUpdate.Description = updateOption.Description
		}

		// Respond with the updated label
		w.WriteHeader(http.StatusOK)
		json.NewEncoder(w).Encode(labelToUpdate)
	})

	server := httptest.NewServer(handler)
	defer server.Close()

	client := NewForgejoClient(server.URL, "")
	labelsConfig := []ForgejoLabel{
		{Name: "area/forgejo", Color: "0e8a16", Description: "Issues related to the Forgejo platform itself."},
		{Name: "area/kubernetes", Color: "0e8a16", Description: "Issues pertaining to Kubernetes integration and deployment."},
		{Name: "new/label", Color: "123456", Description: "A new label to be added."},
	}

	// Perform sync with deleteOtherLabels = false and dryRun = false
	err := EnsureLabelsExist(client, "owner", "repo", labelsConfig, false, false)
	if err != nil {
		t.Fatalf("EnsureLabelsExist returned an error: %v", err)
	}

	// Expected labels after sync without deletion and with updates
	expectedLabels := []ForgejoLabel{
		{ID: 1, Name: "area/forgejo", Color: "0e8a16", Description: "Issues related to the Forgejo platform itself."},                 // Updated description
		{ID: 2, Name: "area/kubernetes", Color: "0e8a16", Description: "Issues pertaining to Kubernetes integration and deployment."}, // No change
		{ID: 3, Name: "obsolete/label", Color: "ff0000", Description: "Obsolete label to be deleted."},                                // Should remain
		{ID: 4, Name: "new/label", Color: "123456", Description: "A new label to be added."},                                          // New label
	}

	// Verify the labels in the mock server
	if !reflect.DeepEqual(existingLabels, expectedLabels) {
		t.Errorf("Expected labels %v, got %v", expectedLabels, existingLabels)
	}
}
