package handlers

import (
	"encoding/csv"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"strconv"
	"strings"

	"github.com/gin-gonic/gin"
	"veza-backend-api/internal/services"
)

// PlaylistImportHandler gère les imports de playlists
// T0494: Create Playlist Import Feature
type PlaylistImportHandler struct {
	playlistService *services.PlaylistService
}

// NewPlaylistImportHandler crée un nouveau handler d'import de playlists
func NewPlaylistImportHandler(playlistService *services.PlaylistService) *PlaylistImportHandler {
	return &PlaylistImportHandler{
		playlistService: playlistService,
	}
}

// ImportPlaylistRequest représente la requête d'import
type ImportPlaylistRequest struct {
	Title       string `json:"title" binding:"required,min=1,max=200"`
	Description string `json:"description,omitempty"`
	IsPublic    bool   `json:"is_public"`
}

// ImportPlaylistJSON importe une playlist depuis un fichier JSON
// T0494: Create Playlist Import Feature
func (h *PlaylistImportHandler) ImportPlaylistJSON(c *gin.Context) {
	userID := c.GetInt64("user_id")
	if userID == 0 {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
		return
	}

	// Récupérer le fichier depuis le formulaire
	file, err := c.FormFile("file")
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "file is required"})
		return
	}

	// Vérifier l'extension
	if !strings.HasSuffix(strings.ToLower(file.Filename), ".json") {
		c.JSON(http.StatusBadRequest, gin.H{"error": "file must be a JSON file"})
		return
	}

	// Ouvrir le fichier
	src, err := file.Open()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to open file"})
		return
	}
	defer src.Close()

	// Lire le contenu
	fileContent, err := io.ReadAll(src)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to read file"})
		return
	}

	// Parser le JSON
	var exportData map[string]interface{}
	if err := json.Unmarshal(fileContent, &exportData); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid JSON format: " + err.Error()})
		return
	}

	// Valider la structure
	if err := h.validateJSONStructure(exportData); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid playlist format: " + err.Error()})
		return
	}

	// Extraire les données de la playlist
	playlistData := exportData["playlist"].(map[string]interface{})
	tracksData := exportData["tracks"].([]interface{})

	// Récupérer le titre depuis le formulaire ou utiliser celui du JSON
	title := c.PostForm("title")
	if title == "" {
		if titleVal, ok := playlistData["title"].(string); ok && titleVal != "" {
			title = titleVal
		} else {
			c.JSON(http.StatusBadRequest, gin.H{"error": "title is required"})
			return
		}
	}

	description := c.PostForm("description")
	if description == "" {
		if descVal, ok := playlistData["description"].(string); ok {
			description = descVal
		}
	}

	isPublic := true
	if isPublicVal, ok := playlistData["is_public"].(bool); ok {
		isPublic = isPublicVal
	} else if isPublicStr := c.PostForm("is_public"); isPublicStr != "" {
		isPublic = isPublicStr == "true"
	}

	// Créer la playlist
	playlist, err := h.playlistService.CreatePlaylist(
		c.Request.Context(),
		userID,
		title,
		description,
		isPublic,
	)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create playlist: " + err.Error()})
		return
	}

	// Importer les tracks
	importedCount, err := h.importTracksFromJSON(c, playlist.ID, tracksData)
	if err != nil {
		// Supprimer la playlist si l'import des tracks échoue
		h.playlistService.DeletePlaylist(c.Request.Context(), playlist.ID, userID)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to import tracks: " + err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"message":        "playlist imported successfully",
		"playlist_id":    playlist.ID,
		"imported_tracks": importedCount,
	})
}

// ImportPlaylistCSV importe une playlist depuis un fichier CSV
// T0494: Create Playlist Import Feature
func (h *PlaylistImportHandler) ImportPlaylistCSV(c *gin.Context) {
	userID := c.GetInt64("user_id")
	if userID == 0 {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
		return
	}

	// Récupérer le fichier depuis le formulaire
	file, err := c.FormFile("file")
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "file is required"})
		return
	}

	// Vérifier l'extension
	if !strings.HasSuffix(strings.ToLower(file.Filename), ".csv") {
		c.JSON(http.StatusBadRequest, gin.H{"error": "file must be a CSV file"})
		return
	}

	// Ouvrir le fichier
	src, err := file.Open()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to open file"})
		return
	}
	defer src.Close()

	// Lire et parser le CSV
	reader := csv.NewReader(src)
	records, err := reader.ReadAll()
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid CSV format: " + err.Error()})
		return
	}

	if len(records) < 2 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "CSV file must contain at least a header and one track"})
		return
	}

	// Valider les en-têtes
	headers := records[0]
	if err := h.validateCSVHeaders(headers); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid CSV headers: " + err.Error()})
		return
	}

	// Récupérer le titre depuis le formulaire
	title := c.PostForm("title")
	if title == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "title is required"})
		return
	}

	description := c.PostForm("description")
	isPublic := true
	if isPublicStr := c.PostForm("is_public"); isPublicStr != "" {
		isPublic = isPublicStr == "true"
	}

	// Créer la playlist
	playlist, err := h.playlistService.CreatePlaylist(
		c.Request.Context(),
		userID,
		title,
		description,
		isPublic,
	)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create playlist: " + err.Error()})
		return
	}

	// Importer les tracks
	importedCount, err := h.importTracksFromCSV(c, playlist.ID, headers, records[1:])
	if err != nil {
		// Supprimer la playlist si l'import des tracks échoue
		h.playlistService.DeletePlaylist(c.Request.Context(), playlist.ID, userID)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to import tracks: " + err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"message":        "playlist imported successfully",
		"playlist_id":    playlist.ID,
		"imported_tracks": importedCount,
	})
}

// validateJSONStructure valide la structure JSON d'une playlist exportée
func (h *PlaylistImportHandler) validateJSONStructure(data map[string]interface{}) error {
	if _, ok := data["playlist"]; !ok {
		return fmt.Errorf("missing 'playlist' field")
	}

	playlistData, ok := data["playlist"].(map[string]interface{})
	if !ok {
		return fmt.Errorf("'playlist' must be an object")
	}

	if _, ok := playlistData["title"]; !ok {
		return fmt.Errorf("missing 'title' in playlist")
	}

	if _, ok := data["tracks"]; !ok {
		return fmt.Errorf("missing 'tracks' field")
	}

	tracksData, ok := data["tracks"].([]interface{})
	if !ok {
		return fmt.Errorf("'tracks' must be an array")
	}

	// Valider chaque track
	for i, track := range tracksData {
		trackMap, ok := track.(map[string]interface{})
		if !ok {
			return fmt.Errorf("track at index %d must be an object", i)
		}

		if _, ok := trackMap["track_id"]; !ok {
			if _, ok := trackMap["id"]; !ok {
				return fmt.Errorf("track at index %d missing 'id' or 'track_id'", i)
			}
		}
	}

	return nil
}

// validateCSVHeaders valide les en-têtes CSV
func (h *PlaylistImportHandler) validateCSVHeaders(headers []string) error {
	requiredHeaders := []string{"Track ID", "Title"}
	foundHeaders := make(map[string]bool)

	for _, header := range headers {
		foundHeaders[strings.TrimSpace(header)] = true
	}

	for _, required := range requiredHeaders {
		if !foundHeaders[required] {
			return fmt.Errorf("missing required header: %s", required)
		}
	}

	return nil
}

// importTracksFromJSON importe les tracks depuis les données JSON
func (h *PlaylistImportHandler) importTracksFromJSON(ctx interface{}, playlistID int64, tracksData []interface{}) (int, error) {
	importedCount := 0

	for _, trackData := range tracksData {
		trackMap := trackData.(map[string]interface{})

		// Récupérer le track_id
		var trackID int64
		if idVal, ok := trackMap["track_id"].(float64); ok {
			trackID = int64(idVal)
		} else if idVal, ok := trackMap["id"].(float64); ok {
			trackID = int64(idVal)
		} else {
			continue // Skip tracks without ID
		}

		// Récupérer la position
		position := importedCount + 1
		if posVal, ok := trackMap["position"].(float64); ok {
			position = int(posVal)
		}

		// Ajouter le track à la playlist
		// Note: On suppose que le track existe déjà dans la base de données
		// Si le track n'existe pas, on le skip
		ginCtx := ctx.(*gin.Context)
		userID := ginCtx.GetInt64("user_id")
		err := h.playlistService.AddTrackToPlaylist(
			ginCtx.Request.Context(),
			playlistID,
			trackID,
			userID,
			position,
		)
		if err != nil {
			// Log l'erreur mais continue avec les autres tracks
			// On pourrait aussi arrêter complètement l'import
			continue
		}

		importedCount++
	}

	return importedCount, nil
}

// importTracksFromCSV importe les tracks depuis les données CSV
func (h *PlaylistImportHandler) importTracksFromCSV(ctx interface{}, playlistID int64, headers []string, records [][]string) (int, error) {
	importedCount := 0

	// Créer un map des index de colonnes
	headerMap := make(map[string]int)
	for i, header := range headers {
		headerMap[strings.TrimSpace(header)] = i
	}

	for _, record := range records {
		if len(record) != len(headers) {
			continue // Skip malformed rows
		}

		// Récupérer le track_id
		trackIDIndex, ok := headerMap["Track ID"]
		if !ok {
			return importedCount, fmt.Errorf("missing 'Track ID' column")
		}

		trackIDStr := strings.TrimSpace(record[trackIDIndex])
		trackID, err := strconv.ParseInt(trackIDStr, 10, 64)
		if err != nil {
			continue // Skip invalid track IDs
		}

		// Récupérer la position
		position := importedCount + 1
		if posIndex, ok := headerMap["Position"]; ok {
			if posStr := strings.TrimSpace(record[posIndex]); posStr != "" {
				if pos, err := strconv.Atoi(posStr); err == nil {
					position = pos
				}
			}
		}

		// Ajouter le track à la playlist
		ginCtx := ctx.(*gin.Context)
		userID := ginCtx.GetInt64("user_id")
		err = h.playlistService.AddTrackToPlaylist(
			ginCtx.Request.Context(),
			playlistID,
			trackID,
			userID,
			position,
		)
		if err != nil {
			// Log l'erreur mais continue avec les autres tracks
			continue
		}

		importedCount++
	}

	return importedCount, nil
}

