package handlers

import (
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/Wikid82/charon/backend/internal/models"
	"github.com/Wikid82/charon/backend/internal/services"
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

func setupDockerTestRouter(t *testing.T) (*gin.Engine, *gorm.DB, *services.RemoteServerService) {
	dsn := "file:" + t.Name() + "?mode=memory&cache=shared"
	db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{})
	require.NoError(t, err)
	require.NoError(t, db.AutoMigrate(&models.RemoteServer{}))

	rsService := services.NewRemoteServerService(db)

	gin.SetMode(gin.TestMode)
	r := gin.New()

	return r, db, rsService
}

func TestDockerHandler_ListContainers(t *testing.T) {
	// We can't easily mock the DockerService without an interface,
	// and the DockerService depends on the real Docker client.
	// So we'll just test that the handler is wired up correctly,
	// even if it returns an error because Docker isn't running in the test env.

	svc, _ := services.NewDockerService()
	// svc might be nil if docker is not available, but NewDockerHandler handles nil?
	// Actually NewDockerHandler just stores it.
	// If svc is nil, ListContainers will panic.
	// So we only run this if svc is not nil.

	if svc == nil {
		t.Skip("Docker not available")
	}

	r, _, rsService := setupDockerTestRouter(t)

	h := NewDockerHandler(svc, rsService)
	h.RegisterRoutes(r.Group("/"))

	req, _ := http.NewRequest("GET", "/docker/containers", http.NoBody)
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	// It might return 200 or 500 depending on if ListContainers succeeds
	assert.Contains(t, []int{http.StatusOK, http.StatusInternalServerError}, w.Code)
}

func TestDockerHandler_ListContainers_NonExistentServerID(t *testing.T) {
	svc, _ := services.NewDockerService()
	if svc == nil {
		t.Skip("Docker not available")
	}

	r, _, rsService := setupDockerTestRouter(t)

	h := NewDockerHandler(svc, rsService)
	h.RegisterRoutes(r.Group("/"))

	// Request with non-existent server_id
	req, _ := http.NewRequest("GET", "/docker/containers?server_id=non-existent-uuid", http.NoBody)
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	assert.Equal(t, http.StatusNotFound, w.Code)
	assert.Contains(t, w.Body.String(), "Remote server not found")
}

func TestDockerHandler_ListContainers_WithServerID(t *testing.T) {
	svc, _ := services.NewDockerService()
	if svc == nil {
		t.Skip("Docker not available")
	}

	r, db, rsService := setupDockerTestRouter(t)

	// Create a remote server
	server := models.RemoteServer{
		UUID:    uuid.New().String(),
		Name:    "Test Docker Server",
		Host:    "docker.example.com",
		Port:    2375,
		Scheme:  "",
		Enabled: true,
	}
	require.NoError(t, db.Create(&server).Error)

	h := NewDockerHandler(svc, rsService)
	h.RegisterRoutes(r.Group("/"))

	// Request with valid server_id (will fail to connect, but shouldn't error on lookup)
	req, _ := http.NewRequest("GET", "/docker/containers?server_id="+server.UUID, http.NoBody)
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	// Should attempt to connect and likely fail with 500 (not 404)
	assert.Contains(t, []int{http.StatusOK, http.StatusInternalServerError}, w.Code)
	if w.Code == http.StatusInternalServerError {
		assert.Contains(t, w.Body.String(), "Failed to list containers")
	}
}

func TestDockerHandler_ListContainers_WithHostQuery(t *testing.T) {
	svc, _ := services.NewDockerService()
	if svc == nil {
		t.Skip("Docker not available")
	}

	r, _, rsService := setupDockerTestRouter(t)

	h := NewDockerHandler(svc, rsService)
	h.RegisterRoutes(r.Group("/"))

	// Request with custom host parameter
	req, _ := http.NewRequest("GET", "/docker/containers?host=tcp://invalid-host:2375", http.NoBody)
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	// Should attempt to connect and fail with 500
	assert.Equal(t, http.StatusInternalServerError, w.Code)
	assert.Contains(t, w.Body.String(), "Failed to list containers")
}

func TestDockerHandler_RegisterRoutes(t *testing.T) {
	svc, _ := services.NewDockerService()
	if svc == nil {
		t.Skip("Docker not available")
	}

	r, _, rsService := setupDockerTestRouter(t)

	h := NewDockerHandler(svc, rsService)
	h.RegisterRoutes(r.Group("/"))

	// Verify route is registered
	routes := r.Routes()
	found := false
	for _, route := range routes {
		if route.Path == "/docker/containers" && route.Method == "GET" {
			found = true
			break
		}
	}
	assert.True(t, found, "Expected /docker/containers GET route to be registered")
}

func TestDockerHandler_NewDockerHandler(t *testing.T) {
	svc, _ := services.NewDockerService()
	if svc == nil {
		t.Skip("Docker not available")
	}

	_, _, rsService := setupDockerTestRouter(t)

	h := NewDockerHandler(svc, rsService)
	assert.NotNil(t, h)
	assert.NotNil(t, h.dockerService)
	assert.NotNil(t, h.remoteServerService)
}
