package services

import (
	"context"
	"crypto/rand"
	"encoding/hex"
	"errors"
	"github.com/google/uuid"
	"time"

	"gorm.io/gorm"
	"veza-backend-api/internal/models"
)

var (
	// ErrPlaylistShareNotFound est retourné quand un share de playlist n'est pas trouvé
	ErrPlaylistShareNotFound = errors.New("playlist share not found")
	// ErrPlaylistShareExpired est retourné quand un share de playlist a expiré
	ErrPlaylistShareExpired = errors.New("playlist share link expired")
)

// PlaylistShareService gère le partage de playlists
// T0488: Create Playlist Public Share Link
type PlaylistShareService struct {
	db *gorm.DB
}

// NewPlaylistShareService crée un nouveau service de partage de playlists
func NewPlaylistShareService(db *gorm.DB) *PlaylistShareService {
	return &PlaylistShareService{db: db}
}

// generateShareToken génère un token unique sécurisé
func generatePlaylistShareToken() (string, error) {
	bytes := make([]byte, 32)
	if _, err := rand.Read(bytes); err != nil {
		return "", err
	}
	return hex.EncodeToString(bytes), nil
}

// CreateShareLink crée un nouveau lien de partage public pour une playlist
// T0488: Create Playlist Public Share Link
// MIGRATION UUID: Completée. playlistID et userID sont des UUIDs.
func (s *PlaylistShareService) CreateShareLink(ctx context.Context, playlistID uuid.UUID, userID uuid.UUID, expiresAt *time.Time) (*models.PlaylistShareLink, error) {
	// Vérifier que la playlist existe et appartient à l'utilisateur
	var playlist models.Playlist
	if err := s.db.First(&playlist, "id = ?", playlistID).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errors.New("playlist not found")
		}
		return nil, err
	}

	// Vérifier que l'utilisateur est le propriétaire ou a la permission admin
	if playlist.UserID != userID {
		// Vérifier si l'utilisateur est collaborateur avec permission admin
		var collaborator models.PlaylistCollaborator
		if err := s.db.Where("playlist_id = ? AND user_id = ? AND permission = ?", playlistID, userID, models.PlaylistPermissionAdmin).First(&collaborator).Error; err != nil {
			if errors.Is(err, gorm.ErrRecordNotFound) {
				return nil, errors.New("forbidden: only owner or admin can create share links")
			}
			return nil, err
		}
	}

	// Vérifier si un lien de partage existe déjà pour cette playlist
	var existingLink models.PlaylistShareLink
	if err := s.db.Where("playlist_id = ? AND deleted_at IS NULL", playlistID).First(&existingLink).Error; err == nil {
		// Un lien existe déjà, vérifier s'il est expiré
		if existingLink.ExpiresAt != nil && existingLink.ExpiresAt.Before(time.Now()) {
			// Le lien est expiré, on le supprime (soft delete) et on en crée un nouveau
			s.db.Delete(&existingLink)
		} else {
			// Le lien existe et est valide, on le retourne
			return &existingLink, nil
		}
	}

	// Générer un token unique
	token, err := generatePlaylistShareToken()
	if err != nil {
		return nil, err
	}

	// Vérifier l'unicité du token (très peu probable mais on vérifie)
	var existingShare models.PlaylistShareLink
	for {
		if err := s.db.Where("share_token = ?", token).First(&existingShare).Error; errors.Is(err, gorm.ErrRecordNotFound) {
			break
		}
		token, err = generatePlaylistShareToken()
		if err != nil {
			return nil, err
		}
	}

	shareLink := &models.PlaylistShareLink{
		PlaylistID:  playlistID,
		UserID:      userID,
		ShareToken:  token,
		ExpiresAt:   expiresAt,
		AccessCount: 0,
	}

	if err := s.db.Create(shareLink).Error; err != nil {
		return nil, err
	}

	return shareLink, nil
}

// ValidateShareToken valide un token de partage et retourne le share link
// T0488: Create Playlist Public Share Link
func (s *PlaylistShareService) ValidateShareToken(ctx context.Context, token string) (*models.PlaylistShareLink, error) {
	var shareLink models.PlaylistShareLink
	if err := s.db.Where("share_token = ? AND deleted_at IS NULL", token).First(&shareLink).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, ErrPlaylistShareNotFound
		}
		return nil, err
	}

	// Vérifier l'expiration
	if shareLink.ExpiresAt != nil && shareLink.ExpiresAt.Before(time.Now()) {
		return nil, ErrPlaylistShareExpired
	}

	// Incrémenter le compteur d'accès
	s.db.Model(&shareLink).Update("access_count", gorm.Expr("access_count + 1"))

	return &shareLink, nil
}

// GetShareLinkByToken récupère un share link par son token (sans incrémenter le compteur)
// T0488: Create Playlist Public Share Link
func (s *PlaylistShareService) GetShareLinkByToken(ctx context.Context, token string) (*models.PlaylistShareLink, error) {
	var shareLink models.PlaylistShareLink
	if err := s.db.Where("share_token = ? AND deleted_at IS NULL", token).First(&shareLink).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, ErrPlaylistShareNotFound
		}
		return nil, err
	}

	// Vérifier l'expiration
	if shareLink.ExpiresAt != nil && shareLink.ExpiresAt.Before(time.Now()) {
		return nil, ErrPlaylistShareExpired
	}

	return &shareLink, nil
}

// RevokeShareLink révoque un lien de partage
// T0488: Create Playlist Public Share Link
func (s *PlaylistShareService) RevokeShareLink(ctx context.Context, shareLinkID, userID uuid.UUID) error {
	var shareLink models.PlaylistShareLink
	if err := s.db.First(&shareLink, "id = ?", shareLinkID).Error; err != nil { // UUID query
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return ErrPlaylistShareNotFound
		}
		return err
	}

	// Vérifier que l'utilisateur est le propriétaire
	if shareLink.UserID != userID {
		return errors.New("forbidden")
	}

	// Soft delete
	return s.db.Delete(&shareLink).Error
}

// GetShareLinkByPlaylistID récupère le lien de partage actif pour une playlist
// T0488: Create Playlist Public Share Link
func (s *PlaylistShareService) GetShareLinkByPlaylistID(ctx context.Context, playlistID uuid.UUID) (*models.PlaylistShareLink, error) {
	var shareLink models.PlaylistShareLink
	if err := s.db.Where("playlist_id = ? AND deleted_at IS NULL", playlistID).First(&shareLink).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, ErrPlaylistShareNotFound
		}
		return nil, err
	}

	// Vérifier l'expiration
	if shareLink.ExpiresAt != nil && shareLink.ExpiresAt.Before(time.Now()) {
		return nil, ErrPlaylistShareExpired
	}

	return &shareLink, nil
}
