package services

import (
	"context"
	"fmt"
	"strings"
	"time"

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

// TrackSearchParams représente les paramètres de recherche de tracks
type TrackSearchParams struct {
	Query       string
	Tags        []string
	TagMode     string // "AND" or "OR"
	MinDuration *int   // seconds
	MaxDuration *int   // seconds
	MinBPM      *int
	MaxBPM      *int
	Genre       *string
	Format      *string
	MinDate     *string // ISO date
	MaxDate     *string // ISO date
	Page        int
	Limit       int
	SortBy      string
	SortOrder   string
}

// TrackSearchService gère la recherche avancée de tracks
type TrackSearchService struct {
	db *gorm.DB
}

// NewTrackSearchService crée un nouveau service de recherche de tracks
func NewTrackSearchService(db *gorm.DB) *TrackSearchService {
	return &TrackSearchService{db: db}
}

// SearchTracks effectue une recherche avancée de tracks avec support de filtres combinés
func (s *TrackSearchService) SearchTracks(ctx context.Context, params TrackSearchParams) ([]*models.Track, int64, error) {
	query := s.db.Model(&models.Track{}).Where("is_public = ? AND deleted_at IS NULL", true)

	// Full-text search on title, artist, album
	if params.Query != "" {
		searchTerm := "%" + strings.ToLower(params.Query) + "%"
		query = query.Where(
			"LOWER(title) LIKE ? OR LOWER(artist) LIKE ? OR LOWER(album) LIKE ?",
			searchTerm, searchTerm, searchTerm,
		)
	}

	// Tag search - Note: Tags field not in current model, skipping for now
	// This can be implemented when tags are added to the Track model
	if len(params.Tags) > 0 {
		// Tags functionality would go here when Tags field is added
		// For now, we'll skip tag filtering
	}

	// Duration filter (supports combined min/max)
	if params.MinDuration != nil && params.MaxDuration != nil {
		// Validate that min <= max
		if *params.MinDuration <= *params.MaxDuration {
			query = query.Where("duration >= ? AND duration <= ?", *params.MinDuration, *params.MaxDuration)
		}
	} else if params.MinDuration != nil {
		query = query.Where("duration >= ?", *params.MinDuration)
	} else if params.MaxDuration != nil {
		query = query.Where("duration <= ?", *params.MaxDuration)
	}

	// BPM filter - Note: BPM field not in current model, skipping for now
	// This can be implemented when BPM field is added to the Track model
	if params.MinBPM != nil || params.MaxBPM != nil {
		// BPM functionality would go here when BPM field is added
		// When implemented, should support combined min/max like duration
	}

	// Genre filter (case-insensitive)
	if params.Genre != nil && *params.Genre != "" {
		query = query.Where("LOWER(genre) = ?", strings.ToLower(strings.TrimSpace(*params.Genre)))
	}

	// Format filter (case-insensitive)
	if params.Format != nil && *params.Format != "" {
		query = query.Where("LOWER(format) = ?", strings.ToLower(strings.TrimSpace(*params.Format)))
	}

	// Date range filter (supports combined min/max)
	if params.MinDate != nil && *params.MinDate != "" {
		minDate, err := time.Parse(time.RFC3339, *params.MinDate)
		if err == nil {
			query = query.Where("created_at >= ?", minDate)
		}
	}
	if params.MaxDate != nil && *params.MaxDate != "" {
		maxDate, err := time.Parse(time.RFC3339, *params.MaxDate)
		if err == nil {
			query = query.Where("created_at <= ?", maxDate)
		}
	}

	// Count total before pagination
	var total int64
	if err := query.Count(&total).Error; err != nil {
		return nil, 0, fmt.Errorf("failed to count tracks: %w", err)
	}

	// Apply sorting with computed fields
	sortOrder := "DESC"
	if params.SortOrder == "asc" {
		sortOrder = "ASC"
	}
	sortBy := params.SortBy
	if sortBy == "" {
		sortBy = "created_at"
	}

	// Handle different sorting options
	switch sortBy {
	case "popularity":
		// Sort by like_count (popularity)
		query = query.Order(fmt.Sprintf("like_count %s", sortOrder))
	case "play_count":
		// Sort by play_count (total plays)
		query = query.Order(fmt.Sprintf("play_count %s", sortOrder))
	case "comment_count":
		// Sort by number of comments (requires join and count)
		query = query.Select("tracks.*, COALESCE(comment_counts.count, 0) as comment_count").
			Joins("LEFT JOIN (SELECT track_id, COUNT(*) as count FROM track_comments WHERE deleted_at IS NULL GROUP BY track_id) as comment_counts ON comment_counts.track_id = tracks.id").
			Order(fmt.Sprintf("comment_count %s", sortOrder))
	case "title":
		// Sort by title alphabetically (case-insensitive)
		query = query.Order(fmt.Sprintf("LOWER(title) %s", sortOrder))
	case "artist":
		// Sort by artist alphabetically (case-insensitive)
		query = query.Order(fmt.Sprintf("LOWER(artist) %s", sortOrder))
	case "created_at", "updated_at", "duration":
		// Direct field sorting
		query = query.Order(fmt.Sprintf("%s %s", sortBy, sortOrder))
	case "like_count":
		// Sort by like_count (same as popularity)
		query = query.Order(fmt.Sprintf("like_count %s", sortOrder))
	default:
		// Default to created_at
		query = query.Order(fmt.Sprintf("created_at %s", sortOrder))
	}

	// Apply pagination
	if params.Page < 1 {
		params.Page = 1
	}
	if params.Limit < 1 {
		params.Limit = 20
	}
	if params.Limit > 100 {
		params.Limit = 100 // Max limit
	}
	offset := (params.Page - 1) * params.Limit
	query = query.Offset(offset).Limit(params.Limit)

	var tracks []*models.Track
	if err := query.Find(&tracks).Error; err != nil {
		return nil, 0, fmt.Errorf("failed to search tracks: %w", err)
	}

	return tracks, total, nil
}
