//go:build integration
// +build integration

package integration

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"go.uber.org/zap"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"

	"veza-backend-api/internal/core/track"
	"veza-backend-api/internal/models"
	"veza-backend-api/internal/services"
	"veza-backend-api/tests/testutils"
)

// TestTrackQuotaEndpoint_GetQuota tests the GET /api/v1/tracks/quota/:id endpoint
// BE-API-026: Verify GET /api/v1/tracks/quota/:id returns correct upload quota info
func TestTrackQuotaEndpoint_GetQuota(t *testing.T) {
	ctx := context.Background()
	gin.SetMode(gin.TestMode)

	// Setup PostgreSQL via testcontainers
	dsn, err := testutils.GetTestContainerDB(ctx)
	if err != nil {
		t.Skipf("Skipping test: PostgreSQL testcontainer not available: %v", err)
		return
	}

	db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
	require.NoError(t, err)

	// Create test user
	userID := uuid.New()
	userIDShort := userID.String()[:8]
	usernameSafe := fmt.Sprintf("test_%s", userIDShort)
	user := &models.User{
		ID:       userID,
		Email:    fmt.Sprintf("test_quota_%s@example.com", userIDShort),
		Username: usernameSafe,
		IsActive: true,
	}
	require.NoError(t, db.Create(user).Error)

	logger := zap.NewNop()
	uploadDir := t.TempDir()

	trackService := track.NewTrackService(db, logger, uploadDir)
	trackUploadService := services.NewTrackUploadService(db, logger)
	chunkService := services.NewTrackChunkService(uploadDir+"/chunks", nil, logger)
	likeService := services.NewTrackLikeService(db, logger)
	streamService := services.NewStreamService("", logger)

	trackHandler := track.NewTrackHandler(trackService, trackUploadService, chunkService, likeService, streamService)

	// Setup router
	router := gin.New()
	router.Use(func(c *gin.Context) {
		c.Set("user_id", userID)
		c.Next()
	})

	api := router.Group("/api/v1")
	api.GET("/tracks/quota/:id", trackHandler.GetUploadQuota)

	// Test 1: Get quota for user with no tracks (empty quota)
	req := httptest.NewRequest("GET", fmt.Sprintf("/api/v1/tracks/quota/%s", userID.String()), nil)
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	require.Equal(t, http.StatusOK, w.Code, "Should return 200 OK: %s", w.Body.String())

	var resp map[string]interface{}
	err = json.Unmarshal(w.Body.Bytes(), &resp)
	require.NoError(t, err)
	require.True(t, resp["success"].(bool))

	data := resp["data"].(map[string]interface{})
	quota := data["quota"].(map[string]interface{})

	// Verify initial quota (empty)
	assert.Equal(t, float64(0), quota["tracks_count"], "Should have 0 tracks initially")
	assert.Equal(t, float64(1000), quota["tracks_limit"], "Should have limit of 1000 tracks")
	assert.Equal(t, float64(0), quota["storage_used"], "Should have 0 bytes used initially")
	assert.Equal(t, float64(100*1024*1024*1024), quota["storage_limit"], "Should have limit of 100GB")

	// Test 2: Create some tracks and verify quota updates
	track1 := &models.Track{
		ID:        uuid.New(),
		Title:     "Test Track 1",
		CreatorID: userID,
		FileSize:  5 * 1024 * 1024, // 5MB
		IsPublic:  true,
	}
	require.NoError(t, db.Create(track1).Error)

	track2 := &models.Track{
		ID:        uuid.New(),
		Title:     "Test Track 2",
		CreatorID: userID,
		FileSize:  10 * 1024 * 1024, // 10MB
		IsPublic:  true,
	}
	require.NoError(t, db.Create(track2).Error)

	// Get quota again
	req2 := httptest.NewRequest("GET", fmt.Sprintf("/api/v1/tracks/quota/%s", userID.String()), nil)
	w2 := httptest.NewRecorder()
	router.ServeHTTP(w2, req2)

	require.Equal(t, http.StatusOK, w2.Code)

	var resp2 map[string]interface{}
	err = json.Unmarshal(w2.Body.Bytes(), &resp2)
	require.NoError(t, err)
	require.True(t, resp2["success"].(bool))

	data2 := resp2["data"].(map[string]interface{})
	quota2 := data2["quota"].(map[string]interface{})

	// Verify updated quota
	assert.Equal(t, float64(2), quota2["tracks_count"], "Should have 2 tracks")
	assert.Equal(t, float64(1000), quota2["tracks_limit"], "Should still have limit of 1000 tracks")
	assert.Equal(t, float64(15*1024*1024), quota2["storage_used"], "Should have 15MB used (5MB + 10MB)")
	assert.Equal(t, float64(100*1024*1024*1024), quota2["storage_limit"], "Should still have limit of 100GB")
}

// TestTrackQuotaEndpoint_WithMe tests the endpoint with "me" parameter
func TestTrackQuotaEndpoint_WithMe(t *testing.T) {
	ctx := context.Background()
	gin.SetMode(gin.TestMode)

	dsn, err := testutils.GetTestContainerDB(ctx)
	if err != nil {
		t.Skipf("Skipping test: PostgreSQL testcontainer not available: %v", err)
		return
	}

	db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
	require.NoError(t, err)

	userID := uuid.New()
	userIDShort := userID.String()[:8]
	usernameSafe := fmt.Sprintf("test_%s", userIDShort)
	user := &models.User{
		ID:       userID,
		Email:    fmt.Sprintf("test_quota_me_%s@example.com", userIDShort),
		Username: usernameSafe,
		IsActive: true,
	}
	require.NoError(t, db.Create(user).Error)

	logger := zap.NewNop()
	uploadDir := t.TempDir()

	trackService := track.NewTrackService(db, logger, uploadDir)
	trackUploadService := services.NewTrackUploadService(db, logger)
	chunkService := services.NewTrackChunkService(uploadDir+"/chunks", nil, logger)
	likeService := services.NewTrackLikeService(db, logger)
	streamService := services.NewStreamService("", logger)

	trackHandler := track.NewTrackHandler(trackService, trackUploadService, chunkService, likeService, streamService)

	router := gin.New()
	router.Use(func(c *gin.Context) {
		c.Set("user_id", userID)
		c.Next()
	})

	api := router.Group("/api/v1")
	api.GET("/tracks/quota/:id", trackHandler.GetUploadQuota)

	// Test with "me" parameter
	req := httptest.NewRequest("GET", "/api/v1/tracks/quota/me", nil)
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	require.Equal(t, http.StatusOK, w.Code, "Should return 200 OK: %s", w.Body.String())

	var resp map[string]interface{}
	err = json.Unmarshal(w.Body.Bytes(), &resp)
	require.NoError(t, err)
	require.True(t, resp["success"].(bool))

	data := resp["data"].(map[string]interface{})
	quota := data["quota"].(map[string]interface{})

	// Verify quota structure
	assert.NotNil(t, quota["tracks_count"])
	assert.NotNil(t, quota["tracks_limit"])
	assert.NotNil(t, quota["storage_used"])
	assert.NotNil(t, quota["storage_limit"])
}

// TestTrackQuotaEndpoint_Unauthorized tests that users cannot see other users' quotas
func TestTrackQuotaEndpoint_Unauthorized(t *testing.T) {
	ctx := context.Background()
	gin.SetMode(gin.TestMode)

	dsn, err := testutils.GetTestContainerDB(ctx)
	if err != nil {
		t.Skipf("Skipping test: PostgreSQL testcontainer not available: %v", err)
		return
	}

	db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
	require.NoError(t, err)

	// Create two users
	user1ID := uuid.New()
	user2ID := uuid.New()

	user1 := &models.User{
		ID:       user1ID,
		Email:    fmt.Sprintf("test_user1_%s@example.com", user1ID.String()[:8]),
		Username: fmt.Sprintf("test_user1_%s", user1ID.String()[:8]),
		IsActive: true,
	}
	user2 := &models.User{
		ID:       user2ID,
		Email:    fmt.Sprintf("test_user2_%s@example.com", user2ID.String()[:8]),
		Username: fmt.Sprintf("test_user2_%s", user2ID.String()[:8]),
		IsActive: true,
	}
	require.NoError(t, db.Create(user1).Error)
	require.NoError(t, db.Create(user2).Error)

	logger := zap.NewNop()
	uploadDir := t.TempDir()

	trackService := track.NewTrackService(db, logger, uploadDir)
	trackUploadService := services.NewTrackUploadService(db, logger)
	chunkService := services.NewTrackChunkService(uploadDir+"/chunks", nil, logger)
	likeService := services.NewTrackLikeService(db, logger)
	streamService := services.NewStreamService("", logger)

	trackHandler := track.NewTrackHandler(trackService, trackUploadService, chunkService, likeService, streamService)

	// User1 tries to access user2's quota
	router := gin.New()
	router.Use(func(c *gin.Context) {
		c.Set("user_id", user1ID) // Authenticated as user1
		c.Next()
	})

	api := router.Group("/api/v1")
	api.GET("/tracks/quota/:id", trackHandler.GetUploadQuota)

	req := httptest.NewRequest("GET", fmt.Sprintf("/api/v1/tracks/quota/%s", user2ID.String()), nil)
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusForbidden, w.Code, "Should return 403 Forbidden for other user's quota")
}

// TestTrackQuotaEndpoint_InvalidUserID tests that invalid user ID returns 400
func TestTrackQuotaEndpoint_InvalidUserID(t *testing.T) {
	ctx := context.Background()
	gin.SetMode(gin.TestMode)

	dsn, err := testutils.GetTestContainerDB(ctx)
	if err != nil {
		t.Skipf("Skipping test: PostgreSQL testcontainer not available: %v", err)
		return
	}

	db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
	require.NoError(t, err)

	userID := uuid.New()
	userIDShort := userID.String()[:8]
	usernameSafe := fmt.Sprintf("test_%s", userIDShort)
	user := &models.User{
		ID:       userID,
		Email:    fmt.Sprintf("test_quota_invalid_%s@example.com", userIDShort),
		Username: usernameSafe,
		IsActive: true,
	}
	require.NoError(t, db.Create(user).Error)

	logger := zap.NewNop()
	uploadDir := t.TempDir()

	trackService := track.NewTrackService(db, logger, uploadDir)
	trackUploadService := services.NewTrackUploadService(db, logger)
	chunkService := services.NewTrackChunkService(uploadDir+"/chunks", nil, logger)
	likeService := services.NewTrackLikeService(db, logger)
	streamService := services.NewStreamService("", logger)

	trackHandler := track.NewTrackHandler(trackService, trackUploadService, chunkService, likeService, streamService)

	router := gin.New()
	router.Use(func(c *gin.Context) {
		c.Set("user_id", userID)
		c.Next()
	})

	api := router.Group("/api/v1")
	api.GET("/tracks/quota/:id", trackHandler.GetUploadQuota)

	// Test with invalid UUID
	req := httptest.NewRequest("GET", "/api/v1/tracks/quota/invalid-uuid", nil)
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusBadRequest, w.Code, "Should return 400 Bad Request for invalid UUID")
}
