package handlers

import (
	"bytes"
	"encoding/json"
	"mime/multipart"
	"net/http"
	"net/http/httptest"
	"strconv"
	"testing"

	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"go.uber.org/zap"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
	"veza-backend-api/internal/models"
	"veza-backend-api/internal/services"
)

func setupTestPlaylistImportHandler(t *testing.T) (*PlaylistImportHandler, *gorm.DB, *models.User, *models.Track, func()) {
	gin.SetMode(gin.TestMode)

	// Setup in-memory SQLite database
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
	require.NoError(t, err)

	// Enable foreign keys for SQLite
	db.Exec("PRAGMA foreign_keys = ON")

	// Auto-migrate
	err = db.AutoMigrate(
		&models.User{},
		&models.Track{},
		&models.Playlist{},
		&models.PlaylistTrack{},
	)
	require.NoError(t, err)

	// Create test user
	user := &models.User{
		Username:     "testuser",
		Email:        "test@example.com",
		PasswordHash: "hash",
		Slug:         "testuser",
		IsActive:     true,
	}
	err = db.Create(user).Error
	require.NoError(t, err)

	// Create test track
	track := &models.Track{
		UserID:    user.ID,
		Title:     "Test Track",
		Artist:    "Test Artist",
		Album:     "Test Album",
		FilePath:  "/path/to/track.mp3",
		FileSize:  1024,
		Format:    "MP3",
		Duration:  180,
		Genre:     "Rock",
		Year:      2020,
		IsPublic:  true,
		Status:    models.TrackStatusCompleted,
		PlayCount: 0,
		LikeCount: 0,
	}
	err = db.Create(track).Error
	require.NoError(t, err)

	// Setup logger
	logger := zap.NewNop()

	// Setup service
	playlistService := services.NewPlaylistServiceWithDB(db, logger)
	handler := NewPlaylistImportHandler(playlistService)

	// Cleanup function
	cleanup := func() {
		// Database will be closed automatically
	}

	return handler, db, user, track, cleanup
}

func TestPlaylistImportHandler_ImportPlaylistJSON(t *testing.T) {
	handler, _, user, track, cleanup := setupTestPlaylistImportHandler(t)
	defer cleanup()

	// Créer les données JSON d'export
	exportData := map[string]interface{}{
		"playlist": map[string]interface{}{
			"id":          int64(1),
			"title":       "Imported Playlist",
			"description": "An imported playlist",
			"is_public":   true,
		},
		"tracks": []interface{}{
			map[string]interface{}{
				"id":       float64(track.ID),
				"title":    track.Title,
				"artist":   track.Artist,
				"position": float64(1),
			},
		},
		"exported_at": "2024-01-01T00:00:00Z",
	}

	jsonData, err := json.Marshal(exportData)
	require.NoError(t, err)

	// Créer un multipart form avec le fichier
	var requestBody bytes.Buffer
	writer := multipart.NewWriter(&requestBody)

	// Ajouter le fichier
	fileWriter, err := writer.CreateFormFile("file", "playlist.json")
	require.NoError(t, err)
	_, err = fileWriter.Write(jsonData)
	require.NoError(t, err)

	// Ajouter le titre
	err = writer.WriteField("title", "Imported Playlist")
	require.NoError(t, err)

	writer.Close()

	// Setup router
	router := gin.New()
	router.POST("/playlists/import/json", func(c *gin.Context) {
		c.Set("user_id", user.ID)
		handler.ImportPlaylistJSON(c)
	})

	// Create request
	req, _ := http.NewRequest("POST", "/playlists/import/json", &requestBody)
	req.Header.Set("Content-Type", writer.FormDataContentType())
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	// Check response
	assert.Equal(t, http.StatusOK, w.Code)

	var response map[string]interface{}
	err = json.Unmarshal(w.Body.Bytes(), &response)
	require.NoError(t, err)

	assert.Equal(t, "playlist imported successfully", response["message"])
	assert.NotNil(t, response["playlist_id"])
	assert.Equal(t, float64(1), response["imported_tracks"])
}

func TestPlaylistImportHandler_ImportPlaylistCSV(t *testing.T) {
	handler, _, user, track, cleanup := setupTestPlaylistImportHandler(t)
	defer cleanup()

	// Créer les données CSV
	csvData := "Position,Track ID,Title,Artist,Album,Duration (seconds),Genre,Year,Added At\n"
	csvData += "1," + strconv.FormatInt(track.ID, 10) + ",Test Track,Test Artist,Test Album,180,Rock,2020,2024-01-01T00:00:00Z\n"

	// Créer un multipart form avec le fichier
	var requestBody bytes.Buffer
	writer := multipart.NewWriter(&requestBody)

	// Ajouter le fichier
	fileWriter, err := writer.CreateFormFile("file", "playlist.csv")
	require.NoError(t, err)
	_, err = fileWriter.Write([]byte(csvData))
	require.NoError(t, err)

	// Ajouter le titre
	err = writer.WriteField("title", "Imported Playlist")
	require.NoError(t, err)

	writer.Close()

	// Setup router
	router := gin.New()
	router.POST("/playlists/import/csv", func(c *gin.Context) {
		c.Set("user_id", user.ID)
		handler.ImportPlaylistCSV(c)
	})

	// Create request
	req, _ := http.NewRequest("POST", "/playlists/import/csv", &requestBody)
	req.Header.Set("Content-Type", writer.FormDataContentType())
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	// Check response
	assert.Equal(t, http.StatusOK, w.Code)

	var response map[string]interface{}
	err = json.Unmarshal(w.Body.Bytes(), &response)
	require.NoError(t, err)

	assert.Equal(t, "playlist imported successfully", response["message"])
	assert.NotNil(t, response["playlist_id"])
	assert.Equal(t, float64(1), response["imported_tracks"])
}

func TestPlaylistImportHandler_ImportPlaylistJSON_InvalidFile(t *testing.T) {
	handler, _, user, _, cleanup := setupTestPlaylistImportHandler(t)
	defer cleanup()

	// Créer un multipart form avec un fichier invalide
	var requestBody bytes.Buffer
	writer := multipart.NewWriter(&requestBody)

	// Ajouter un fichier texte invalide
	fileWriter, err := writer.CreateFormFile("file", "playlist.txt")
	require.NoError(t, err)
	_, err = fileWriter.Write([]byte("invalid content"))
	require.NoError(t, err)

	writer.Close()

	// Setup router
	router := gin.New()
	router.POST("/playlists/import/json", func(c *gin.Context) {
		c.Set("user_id", user.ID)
		handler.ImportPlaylistJSON(c)
	})

	// Create request
	req, _ := http.NewRequest("POST", "/playlists/import/json", &requestBody)
	req.Header.Set("Content-Type", writer.FormDataContentType())
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	// Check response
	assert.Equal(t, http.StatusBadRequest, w.Code)
}

func TestPlaylistImportHandler_ImportPlaylistJSON_InvalidJSON(t *testing.T) {
	handler, _, user, _, cleanup := setupTestPlaylistImportHandler(t)
	defer cleanup()

	// Créer un multipart form avec un JSON invalide
	var requestBody bytes.Buffer
	writer := multipart.NewWriter(&requestBody)

	// Ajouter un fichier JSON invalide
	fileWriter, err := writer.CreateFormFile("file", "playlist.json")
	require.NoError(t, err)
	_, err = fileWriter.Write([]byte("{ invalid json }"))
	require.NoError(t, err)

	writer.Close()

	// Setup router
	router := gin.New()
	router.POST("/playlists/import/json", func(c *gin.Context) {
		c.Set("user_id", user.ID)
		handler.ImportPlaylistJSON(c)
	})

	// Create request
	req, _ := http.NewRequest("POST", "/playlists/import/json", &requestBody)
	req.Header.Set("Content-Type", writer.FormDataContentType())
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	// Check response
	assert.Equal(t, http.StatusBadRequest, w.Code)
}

func TestPlaylistImportHandler_ImportPlaylistJSON_MissingTitle(t *testing.T) {
	handler, _, user, _, cleanup := setupTestPlaylistImportHandler(t)
	defer cleanup()

	// Créer les données JSON sans titre dans le formulaire
	exportData := map[string]interface{}{
		"playlist": map[string]interface{}{
			"id":          int64(1),
			"description": "An imported playlist",
			"is_public":   true,
		},
		"tracks": []interface{}{},
	}

	jsonData, err := json.Marshal(exportData)
	require.NoError(t, err)

	// Créer un multipart form avec le fichier
	var requestBody bytes.Buffer
	writer := multipart.NewWriter(&requestBody)

	// Ajouter le fichier
	fileWriter, err := writer.CreateFormFile("file", "playlist.json")
	require.NoError(t, err)
	_, err = fileWriter.Write(jsonData)
	require.NoError(t, err)

	// Ne pas ajouter le titre
	writer.Close()

	// Setup router
	router := gin.New()
	router.POST("/playlists/import/json", func(c *gin.Context) {
		c.Set("user_id", user.ID)
		handler.ImportPlaylistJSON(c)
	})

	// Create request
	req, _ := http.NewRequest("POST", "/playlists/import/json", &requestBody)
	req.Header.Set("Content-Type", writer.FormDataContentType())
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	// Check response
	assert.Equal(t, http.StatusBadRequest, w.Code)
}

func TestPlaylistImportHandler_ImportPlaylistCSV_InvalidHeaders(t *testing.T) {
	handler, _, user, _, cleanup := setupTestPlaylistImportHandler(t)
	defer cleanup()

	// Créer les données CSV avec des en-têtes invalides
	csvData := "Invalid,Headers\n"
	csvData += "1,2\n"

	// Créer un multipart form avec le fichier
	var requestBody bytes.Buffer
	writer := multipart.NewWriter(&requestBody)

	// Ajouter le fichier
	fileWriter, err := writer.CreateFormFile("file", "playlist.csv")
	require.NoError(t, err)
	_, err = fileWriter.Write([]byte(csvData))
	require.NoError(t, err)

	// Ajouter le titre
	err = writer.WriteField("title", "Imported Playlist")
	require.NoError(t, err)

	writer.Close()

	// Setup router
	router := gin.New()
	router.POST("/playlists/import/csv", func(c *gin.Context) {
		c.Set("user_id", user.ID)
		handler.ImportPlaylistCSV(c)
	})

	// Create request
	req, _ := http.NewRequest("POST", "/playlists/import/csv", &requestBody)
	req.Header.Set("Content-Type", writer.FormDataContentType())
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	// Check response
	assert.Equal(t, http.StatusBadRequest, w.Code)
}

