package services

import (
	"context"
	"crypto/rand"
	"encoding/hex"
	"errors"
	"fmt"
	"strings"
	"time"

	"veza-backend-api/internal/models"

	"github.com/google/uuid"
	"gorm.io/gorm"
)

var (
	// ErrShareNotFound est retourné quand un share n'est pas trouvé
	ErrShareNotFound = errors.New("share not found")
	// ErrShareExpired est retourné quand un share a expiré
	ErrShareExpired = errors.New("share link expired")
	// ErrSharePermissionDenied est retourné quand la permission demandée n'est pas accordée
	ErrSharePermissionDenied = errors.New("permission denied")
)

// TrackShareService gère le partage de tracks
type TrackShareService struct {
	db *gorm.DB
}

// NewTrackShareService crée un nouveau service de partage de tracks
func NewTrackShareService(db *gorm.DB) *TrackShareService {
	return &TrackShareService{db: db}
}

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

// CreateShare crée un nouveau lien de partage pour un track
// MIGRATION UUID: Completée. UserID et TrackID en UUID.
func (s *TrackShareService) CreateShare(ctx context.Context, trackID uuid.UUID, userID uuid.UUID, permissions string, expiresAt *time.Time) (*models.TrackShare, error) {
	// Vérifier que le track existe et appartient à l'utilisateur
	var track models.Track
	if err := s.db.First(&track, "id = ?", trackID).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, ErrTrackNotFound
		}
		return nil, err
	}
	if track.UserID != userID {
		return nil, ErrForbidden
	}

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

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

	share := &models.TrackShare{
		TrackID:     trackID,
		UserID:      userID,
		ShareToken:  token,
		Permissions: permissions,
		ExpiresAt:   expiresAt,
		AccessCount: 0,
	}

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

	return share, nil
}

// ValidateShareToken valide un token de partage et retourne le share
func (s *TrackShareService) ValidateShareToken(ctx context.Context, token string) (*models.TrackShare, error) {
	var share models.TrackShare
	if err := s.db.Where("share_token = ? AND deleted_at IS NULL", token).First(&share).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, ErrShareNotFound
		}
		return nil, err
	}

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

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

	// Recharger l'objet pour obtenir la valeur mise à jour
	if err := s.db.First(&share, share.ID).Error; err != nil {
		return nil, fmt.Errorf("failed to reload share: %w", err)
	}

	return &share, nil
}

// CheckPermission vérifie si un share a une permission spécifique
func (s *TrackShareService) CheckPermission(share *models.TrackShare, permission string) bool {
	if share == nil {
		return false
	}

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

	// Vérifier les permissions
	permissions := strings.Split(share.Permissions, ",")
	for _, p := range permissions {
		if strings.TrimSpace(strings.ToLower(p)) == strings.ToLower(permission) {
			return true
		}
	}

	return false
}

// GetShareByToken récupère un share par son token (sans incrémenter le compteur)
func (s *TrackShareService) GetShareByToken(ctx context.Context, token string) (*models.TrackShare, error) {
	var share models.TrackShare
	if err := s.db.Where("share_token = ? AND deleted_at IS NULL", token).First(&share).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, ErrShareNotFound
		}
		return nil, err
	}

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

	return &share, nil
}

// RevokeShare révoque un lien de partage
// MIGRATION UUID: Completée. UserID et ShareID en UUID.
func (s *TrackShareService) RevokeShare(ctx context.Context, shareID uuid.UUID, userID uuid.UUID) error {
	var share models.TrackShare
	if err := s.db.First(&share, "id = ?", shareID).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return ErrShareNotFound
		}
		return err
	}

	// Vérifier que l'utilisateur est le propriétaire
	if share.UserID != userID {
		return ErrForbidden
	}

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