//go:build !integration
// +build !integration

package track

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

	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
)

// TestTrackHandler_NoPanicWhenUserIDMissing vérifie qu'aucun handler ne panic si user_id est absent du contexte
func TestTrackHandler_NoPanicWhenUserIDMissing(t *testing.T) {
	gin.SetMode(gin.TestMode)
	handler, _, _, cleanup := setupTestTrackHandlerOwnership(t)
	defer cleanup()

	// Liste des handlers à tester (ceux qui utilisent getUserID)
	testCases := []struct {
		name   string
		method string
		path   string
		body   string
	}{
		{"UploadTrack", "POST", "/tracks", ""},
		{"InitiateChunkedUpload", "POST", "/tracks/initiate", `{"total_chunks": 1, "total_size": 1000, "filename": "test.mp3"}`},
		{"UploadChunk", "POST", "/tracks/chunk", ""},
		{"CompleteChunkedUpload", "POST", "/tracks/complete", `{"upload_id": "test-id"}`},
		{"ResumeUpload", "GET", "/tracks/resume/test-id", ""},
		{"UpdateTrack", "PUT", "/tracks/test-id", `{"title": "Test"}`},
		{"DeleteTrack", "DELETE", "/tracks/test-id", ""},
		{"BatchDeleteTracks", "POST", "/tracks/batch/delete", `{"track_ids": ["test-id"]}`},
		{"BatchUpdateTracks", "POST", "/tracks/batch/update", `{"track_ids": ["test-id"], "updates": {"title": "Test"}}`},
		{"LikeTrack", "POST", "/tracks/test-id/like", ""},
		{"UnlikeTrack", "DELETE", "/tracks/test-id/like", ""},
		{"CreateShare", "POST", "/tracks/test-id/share", `{"permissions": "read"}`},
		{"RevokeShare", "DELETE", "/tracks/share/test-id", ""},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			// Créer une requête SANS user_id dans le contexte (simule middleware auth absent/bypass)
			req := httptest.NewRequest(tc.method, tc.path, nil)
			if tc.body != "" {
				req.Header.Set("Content-Type", "application/json")
				req.Body = http.NoBody // Simplification pour le test
			}
			w := httptest.NewRecorder()
			c, _ := gin.CreateTestContext(w)
			c.Request = req
			// Ne PAS définir "user_id" dans le contexte

			// Exécuter le handler - ne doit PAS panic
			// On utilise recover pour capturer un éventuel panic
			var panicked bool
			var panicValue interface{}
			func() {
				defer func() {
					if r := recover(); r != nil {
						panicked = true
						panicValue = r
					}
				}()

				switch tc.name {
				case "UploadTrack":
					handler.UploadTrack(c)
				case "InitiateChunkedUpload":
					handler.InitiateChunkedUpload(c)
				case "UploadChunk":
					handler.UploadChunk(c)
				case "CompleteChunkedUpload":
					handler.CompleteChunkedUpload(c)
				case "ResumeUpload":
					c.Params = gin.Params{gin.Param{Key: "uploadId", Value: "test-id"}}
					handler.ResumeUpload(c)
				case "UpdateTrack":
					c.Params = gin.Params{gin.Param{Key: "id", Value: "test-id"}}
					handler.UpdateTrack(c)
				case "DeleteTrack":
					c.Params = gin.Params{gin.Param{Key: "id", Value: "test-id"}}
					handler.DeleteTrack(c)
				case "BatchDeleteTracks":
					handler.BatchDeleteTracks(c)
				case "BatchUpdateTracks":
					handler.BatchUpdateTracks(c)
				case "LikeTrack":
					c.Params = gin.Params{gin.Param{Key: "id", Value: "test-id"}}
					handler.LikeTrack(c)
				case "UnlikeTrack":
					c.Params = gin.Params{gin.Param{Key: "id", Value: "test-id"}}
					handler.UnlikeTrack(c)
				case "CreateShare":
					c.Params = gin.Params{gin.Param{Key: "id", Value: "test-id"}}
					handler.CreateShare(c)
				case "RevokeShare":
					c.Params = gin.Params{gin.Param{Key: "id", Value: "test-id"}}
					handler.RevokeShare(c)
				}
			}()

			// Vérifier qu'il n'y a pas eu de panic
			assert.False(t, panicked, "Handler %s a paniqué avec user_id absent: %v", tc.name, panicValue)

			// Vérifier que la réponse est 401 Unauthorized ou 403 Forbidden (pas de panic, erreur structurée)
			// MOD-P1-RES-001: getUserID() utilise maintenant RespondWithAppError qui peut retourner 401 ou 403
			assert.True(t, w.Code == http.StatusUnauthorized || w.Code == http.StatusForbidden,
				"Handler %s devrait retourner 401 ou 403 si user_id absent, got %d", tc.name, w.Code)

			// Vérifier que le code de statut est correct (pas de panic, erreur structurée)
			// Le handler a dû répondre avec 401 via RespondWithAppError (format standardisé)
		})
	}
}

// TestTrackHandler_GetUserID_Returns401WhenMissing vérifie spécifiquement le helper getUserID
func TestTrackHandler_GetUserID_Returns401WhenMissing(t *testing.T) {
	gin.SetMode(gin.TestMode)
	handler, _, _, cleanup := setupTestTrackHandlerOwnership(t)
	defer cleanup()

	req := httptest.NewRequest("GET", "/test", nil)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req
	// Ne PAS définir "user_id" dans le contexte

	// Appeler getUserID directement (via un handler de test)
	var panicked bool
	var ok bool

	func() {
		defer func() {
			if r := recover(); r != nil {
				panicked = true
			}
		}()
		_, ok = handler.getUserID(c)
	}()

	// Vérifier qu'il n'y a pas eu de panic
	assert.False(t, panicked, "getUserID ne doit pas panic si user_id est absent")

	// Vérifier que ok est false
	assert.False(t, ok, "getUserID doit retourner false si user_id est absent")

	// Vérifier que la réponse est 401 ou 403 (RespondWithAppError peut mapper ErrCodeUnauthorized vers 403)
	assert.True(t, w.Code == http.StatusUnauthorized || w.Code == http.StatusForbidden,
		"getUserID doit retourner 401 ou 403 si user_id est absent, got %d", w.Code)
}

// TestTrackHandler_GetUserID_Returns401WhenInvalidType vérifie que getUserID gère les types invalides
func TestTrackHandler_GetUserID_Returns401WhenInvalidType(t *testing.T) {
	gin.SetMode(gin.TestMode)
	handler, _, _, cleanup := setupTestTrackHandlerOwnership(t)
	defer cleanup()

	req := httptest.NewRequest("GET", "/test", nil)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req
	// Définir user_id avec un type invalide (string au lieu de uuid.UUID)
	c.Set("user_id", "invalid-type")

	var panicked bool
	var ok bool

	func() {
		defer func() {
			if r := recover(); r != nil {
				panicked = true
			}
		}()
		_, ok = handler.getUserID(c)
	}()

	// Vérifier qu'il n'y a pas eu de panic
	assert.False(t, panicked, "getUserID ne doit pas panic si user_id a un type invalide")

	// Vérifier que ok est false
	assert.False(t, ok, "getUserID doit retourner false si user_id a un type invalide")

	// Vérifier que la réponse est 401 ou 403 (RespondWithAppError peut mapper ErrCodeUnauthorized vers 403)
	assert.True(t, w.Code == http.StatusUnauthorized || w.Code == http.StatusForbidden,
		"getUserID doit retourner 401 ou 403 si user_id a un type invalide, got %d", w.Code)
}
