package handlers

import (
	"bytes"
	"context"
	"encoding/csv"
	"encoding/json"
	"net/http"
	"strconv"
	"time"

	"github.com/google/uuid"

	"veza-backend-api/internal/models"
	"veza-backend-api/internal/services"

	"github.com/gin-gonic/gin"
)

// PlaylistExportServiceInterface defines methods required by PlaylistExportHandler
type PlaylistExportServiceInterface interface {
	GetPlaylist(ctx context.Context, playlistID uuid.UUID, userID *uuid.UUID) (*models.Playlist, error)
	CheckPermission(ctx context.Context, playlistID uuid.UUID, userID uuid.UUID, requiredPermission models.PlaylistPermission) (bool, error)
}

// PlaylistExportHandler gère les exports de playlists
// T0493: Create Playlist Export Feature
type PlaylistExportHandler struct {
	playlistService PlaylistExportServiceInterface
}

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

// NewPlaylistExportHandlerWithInterface creates a new handler with interface for testing
func NewPlaylistExportHandlerWithInterface(playlistService PlaylistExportServiceInterface) *PlaylistExportHandler {
	return &PlaylistExportHandler{
		playlistService: playlistService,
	}
}

// ExportPlaylistJSON exporte une playlist au format JSON
// T0493: Create Playlist Export Feature
func (h *PlaylistExportHandler) ExportPlaylistJSON(c *gin.Context) {
	playlistID, err := uuid.Parse(c.Param("id")) // Changed to uuid.Parse
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid playlist id"})
		return
	}

	// Vérifier que la playlist existe et que l'utilisateur a accès
	var userID *uuid.UUID
	if uidInterface, exists := c.Get("user_id"); exists {
		if uid, ok := uidInterface.(uuid.UUID); ok {
			userID = &uid
		}
	}

	playlist, err := h.playlistService.GetPlaylist(c.Request.Context(), playlistID, userID)
	if err != nil {
		if err.Error() == "playlist not found" {
			c.JSON(http.StatusNotFound, gin.H{"error": "playlist not found"})
			return
		}
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	// Vérifier que l'utilisateur a accès (propriétaire, collaborateur ou playlist publique)
	currentUserID := uuid.Nil
	if userID != nil {
		currentUserID = *userID
	}

	if playlist.UserID != currentUserID && !playlist.IsPublic {
		// Vérifier si l'utilisateur est collaborateur
		if userID != nil {
			hasAccess, err := h.playlistService.CheckPermission(c.Request.Context(), playlistID, *userID, models.PlaylistPermissionRead)
			if err != nil || !hasAccess {
				c.JSON(http.StatusForbidden, gin.H{"error": "forbidden"})
				return
			}
		} else {
			c.JSON(http.StatusForbidden, gin.H{"error": "forbidden"})
			return
		}
	}

	// Préparer les données d'export
	exportData := map[string]interface{}{
		"playlist": map[string]interface{}{
			"id":          playlist.ID,
			"title":       playlist.Title,
			"description": playlist.Description,
			"is_public":   playlist.IsPublic,
			"cover_url":   playlist.CoverURL,
			"track_count": playlist.TrackCount,
			"created_at":  playlist.CreatedAt,
			"updated_at":  playlist.UpdatedAt,
		},
		"tracks":      make([]map[string]interface{}, 0),
		"exported_at": time.Now().Format(time.RFC3339),
	}

	// Ajouter les tracks avec leurs informations
	if playlist.Tracks != nil {
		for _, playlistTrack := range playlist.Tracks {
			// Track est un struct (non-pointer), toujours valide
			{
				trackData := map[string]interface{}{
					"position": playlistTrack.Position,
					"id":       playlistTrack.Track.ID,
					"title":    playlistTrack.Track.Title,
					"artist":   playlistTrack.Track.Artist,
					"album":    playlistTrack.Track.Album,
					"duration": playlistTrack.Track.Duration,
					"genre":    playlistTrack.Track.Genre,
					"year":     playlistTrack.Track.Year,
					"added_at": playlistTrack.AddedAt,
				}
				exportData["tracks"] = append(exportData["tracks"].([]map[string]interface{}), trackData)
			}
		}
	}

	// Convertir en JSON
	jsonData, err := json.MarshalIndent(exportData, "", "  ")
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to generate JSON export"})
		return
	}

	// Définir les headers pour le téléchargement
	filename := "playlist_" + playlistID.String() + "_" + time.Now().Format("20060102") + ".json" // Changed to playlistID.String()
	c.Header("Content-Type", "application/json")
	c.Header("Content-Disposition", "attachment; filename="+filename)
	c.Data(http.StatusOK, "application/json", jsonData)
}

// ExportPlaylistCSV exporte une playlist au format CSV
// T0493: Create Playlist Export Feature
func (h *PlaylistExportHandler) ExportPlaylistCSV(c *gin.Context) {
	playlistID, err := uuid.Parse(c.Param("id")) // Changed to uuid.Parse
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid playlist id"})
		return
	}

	// Vérifier que la playlist existe et que l'utilisateur a accès
	var userID *uuid.UUID
	if uidInterface, exists := c.Get("user_id"); exists {
		if uid, ok := uidInterface.(uuid.UUID); ok {
			userID = &uid
		}
	}

	playlist, err := h.playlistService.GetPlaylist(c.Request.Context(), playlistID, userID)
	if err != nil {
		if err.Error() == "playlist not found" {
			c.JSON(http.StatusNotFound, gin.H{"error": "playlist not found"})
			return
		}
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	// Vérifier que l'utilisateur a accès (propriétaire, collaborateur ou playlist publique)
	currentUserID := uuid.Nil
	if userID != nil {
		currentUserID = *userID
	}

	if playlist.UserID != currentUserID && !playlist.IsPublic {
		// Vérifier si l'utilisateur est collaborateur
		if userID != nil {
			hasAccess, err := h.playlistService.CheckPermission(c.Request.Context(), playlistID, *userID, models.PlaylistPermissionRead)
			if err != nil || !hasAccess {
				c.JSON(http.StatusForbidden, gin.H{"error": "forbidden"})
				return
			}
		} else {
			c.JSON(http.StatusForbidden, gin.H{"error": "forbidden"})
			return
		}
	}

	// Créer le buffer CSV
	var csvData [][]string

	// En-têtes
	csvData = append(csvData, []string{
		"Position",
		"Track ID",
		"Title",
		"Artist",
		"Album",
		"Duration (seconds)",
		"Genre",
		"Year",
		"Added At",
	})

	// Ajouter les tracks
	if playlist.Tracks != nil {
		for _, playlistTrack := range playlist.Tracks {
			// Track est un struct (non-pointer), toujours valide
			{
				row := []string{
					strconv.Itoa(playlistTrack.Position),
					playlistTrack.Track.ID.String(), // Changed to playlistTrack.Track.ID.String()
					playlistTrack.Track.Title,
					playlistTrack.Track.Artist,
					playlistTrack.Track.Album,
					strconv.Itoa(playlistTrack.Track.Duration),
					playlistTrack.Track.Genre,
					strconv.Itoa(playlistTrack.Track.Year),
					playlistTrack.AddedAt.Format(time.RFC3339),
				}
				csvData = append(csvData, row)
			}
		}
	}

	// Générer le CSV
	var csvBuffer bytes.Buffer
	writer := csv.NewWriter(&csvBuffer)

	// Écrire toutes les lignes
	for _, row := range csvData {
		if err := writer.Write(row); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to generate CSV export"})
			return
		}
	}
	writer.Flush()

	if err := writer.Error(); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to generate CSV export"})
		return
	}

	// Définir les headers pour le téléchargement
	filename := "playlist_" + playlistID.String() + "_" + time.Now().Format("20060102") + ".csv" // Changed to playlistID.String()
	c.Header("Content-Type", "text/csv")
	c.Header("Content-Disposition", "attachment; filename="+filename)
	c.Data(http.StatusOK, "text/csv", csvBuffer.Bytes())
}
