package education

import (
	"context"
	"fmt"
	"sync"
	"time"

	"github.com/google/uuid"
	"go.uber.org/zap"
)

// Course représente un cours de formation
type Course struct {
	ID           string         `json:"id"`
	Title        string         `json:"title"`
	Description  string         `json:"description"`
	Instructor   string         `json:"instructor"`
	Category     string         `json:"category"`
	Level        CourseLevel    `json:"level"`
	Duration     time.Duration  `json:"duration"`
	Price        float64        `json:"price"`
	Currency     string         `json:"currency"`
	Language     string         `json:"language"`
	Thumbnail    string         `json:"thumbnail"`
	VideoURL     string         `json:"video_url"`
	Lessons      []*Lesson      `json:"lessons"`
	Exercises    []*Exercise    `json:"exercises"`
	Certificates []*Certificate `json:"certificates"`
	Tags         []string       `json:"tags"`
	IsPublished  bool           `json:"is_published"`
	IsFree       bool           `json:"is_free"`
	CreatedAt    time.Time      `json:"created_at"`
	UpdatedAt    time.Time      `json:"updated_at"`
	mu           sync.RWMutex
}

// CourseLevel définit le niveau de difficulté d'un cours
type CourseLevel string

const (
	CourseLevelBeginner     CourseLevel = "beginner"
	CourseLevelIntermediate CourseLevel = "intermediate"
	CourseLevelAdvanced     CourseLevel = "advanced"
	CourseLevelExpert       CourseLevel = "expert"
)

// Lesson représente une leçon dans un cours
type Lesson struct {
	ID          string        `json:"id"`
	CourseID    string        `json:"course_id"`
	Title       string        `json:"title"`
	Description string        `json:"description"`
	Content     string        `json:"content"`
	VideoURL    string        `json:"video_url"`
	Duration    time.Duration `json:"duration"`
	Order       int           `json:"order"`
	IsFree      bool          `json:"is_free"`
	CreatedAt   time.Time     `json:"created_at"`
	UpdatedAt   time.Time     `json:"updated_at"`
}

// Exercise représente un exercice pratique
type Exercise struct {
	ID          string        `json:"id"`
	CourseID    string        `json:"course_id"`
	LessonID    string        `json:"lesson_id"`
	Title       string        `json:"title"`
	Description string        `json:"description"`
	Type        ExerciseType  `json:"type"`
	Content     string        `json:"content"`
	Solution    string        `json:"solution"`
	Points      int           `json:"points"`
	TimeLimit   time.Duration `json:"time_limit"`
	IsRequired  bool          `json:"is_required"`
	CreatedAt   time.Time     `json:"created_at"`
	UpdatedAt   time.Time     `json:"updated_at"`
}

// ExerciseType définit le type d'exercice
type ExerciseType string

const (
	ExerciseTypeQuiz    ExerciseType = "quiz"
	ExerciseTypeProject ExerciseType = "project"
	ExerciseTypeAudio   ExerciseType = "audio"
	ExerciseTypeCode    ExerciseType = "code"
	ExerciseTypeEssay   ExerciseType = "essay"
)

// Certificate représente un certificat de formation
type Certificate struct {
	ID          string    `json:"id"`
	CourseID    string    `json:"course_id"`
	UserID      uuid.UUID `json:"user_id"`
	Title       string    `json:"title"`
	Description string    `json:"description"`
	Score       float64   `json:"score"`
	MaxScore    float64   `json:"max_score"`
	IsPassed    bool      `json:"is_passed"`
	IssuedAt    time.Time `json:"issued_at"`
	ExpiresAt   time.Time `json:"expires_at"`
	CreatedAt   time.Time `json:"created_at"`
}

// CourseProgress représente la progression d'un utilisateur dans un cours
type CourseProgress struct {
	ID               string        `json:"id"`
	UserID           uuid.UUID     `json:"user_id"`
	CourseID         string        `json:"course_id"`
	Progress         float64       `json:"progress"` // 0.0 à 1.0
	CompletedLessons []string      `json:"completed_lessons"`
	CurrentLesson    string        `json:"current_lesson"`
	Score            float64       `json:"score"`
	TimeSpent        time.Duration `json:"time_spent"`
	LastAccessed     time.Time     `json:"last_accessed"`
	IsCompleted      bool          `json:"is_completed"`
	CompletedAt      time.Time     `json:"completed_at"`
	CreatedAt        time.Time     `json:"created_at"`
	UpdatedAt        time.Time     `json:"updated_at"`
}

// CourseManager gère les cours et formations
type CourseManager struct {
	courses  map[string]*Course
	progress map[string]*CourseProgress
	logger   *zap.Logger
	mu       sync.RWMutex
}

// NewCourseManager crée un nouveau gestionnaire de cours
func NewCourseManager(logger *zap.Logger) *CourseManager {
	return &CourseManager{
		courses:  make(map[string]*Course),
		progress: make(map[string]*CourseProgress),
		logger:   logger,
	}
}

// CreateCourse crée un nouveau cours
func (cm *CourseManager) CreateCourse(ctx context.Context, title, description, instructor, category string, level CourseLevel, duration time.Duration, price float64, language string) (*Course, error) {
	cm.mu.Lock()
	defer cm.mu.Unlock()

	courseID := uuid.New().String()

	course := &Course{
		ID:           courseID,
		Title:        title,
		Description:  description,
		Instructor:   instructor,
		Category:     category,
		Level:        level,
		Duration:     duration,
		Price:        price,
		Currency:     "EUR",
		Language:     language,
		Lessons:      []*Lesson{},
		Exercises:    []*Exercise{},
		Certificates: []*Certificate{},
		Tags:         []string{},
		IsPublished:  false,
		IsFree:       price == 0,
		CreatedAt:    time.Now(),
		UpdatedAt:    time.Now(),
	}

	cm.courses[courseID] = course

	cm.logger.Info("Cours créé",
		zap.String("course_id", courseID),
		zap.String("title", title),
		zap.String("instructor", instructor))

	return course, nil
}

// GetCourse récupère un cours par son ID
func (cm *CourseManager) GetCourse(ctx context.Context, courseID string) (*Course, error) {
	cm.mu.RLock()
	defer cm.mu.RUnlock()

	course, exists := cm.courses[courseID]
	if !exists {
		return nil, fmt.Errorf("cours non trouvé: %s", courseID)
	}

	return course, nil
}

// ListCourses liste tous les cours disponibles
func (cm *CourseManager) ListCourses(ctx context.Context, filters map[string]interface{}) ([]*Course, error) {
	cm.mu.RLock()
	defer cm.mu.RUnlock()

	var courses []*Course
	for _, course := range cm.courses {
		// Appliquer les filtres si fournis
		if filters != nil {
			if category, ok := filters["category"].(string); ok && course.Category != category {
				continue
			}
			if level, ok := filters["level"].(CourseLevel); ok && course.Level != level {
				continue
			}
			if isPublished, ok := filters["is_published"].(bool); ok && course.IsPublished != isPublished {
				continue
			}
			if isFree, ok := filters["is_free"].(bool); ok && course.IsFree != isFree {
				continue
			}
		}
		courses = append(courses, course)
	}

	return courses, nil
}

// UpdateCourse met à jour un cours
func (cm *CourseManager) UpdateCourse(ctx context.Context, courseID string, updates map[string]interface{}) (*Course, error) {
	cm.mu.Lock()
	defer cm.mu.Unlock()

	course, exists := cm.courses[courseID]
	if !exists {
		return nil, fmt.Errorf("cours non trouvé: %s", courseID)
	}

	// Appliquer les mises à jour
	if title, ok := updates["title"].(string); ok {
		course.Title = title
	}
	if description, ok := updates["description"].(string); ok {
		course.Description = description
	}
	if instructor, ok := updates["instructor"].(string); ok {
		course.Instructor = instructor
	}
	if category, ok := updates["category"].(string); ok {
		course.Category = category
	}
	if level, ok := updates["level"].(CourseLevel); ok {
		course.Level = level
	}
	if duration, ok := updates["duration"].(time.Duration); ok {
		course.Duration = duration
	}
	if price, ok := updates["price"].(float64); ok {
		course.Price = price
		course.IsFree = price == 0
	}
	if isPublished, ok := updates["is_published"].(bool); ok {
		course.IsPublished = isPublished
	}

	course.UpdatedAt = time.Now()

	cm.logger.Info("Cours mis à jour",
		zap.String("course_id", courseID),
		zap.String("title", course.Title))

	return course, nil
}

// DeleteCourse supprime un cours
func (cm *CourseManager) DeleteCourse(ctx context.Context, courseID string) error {
	cm.mu.Lock()
	defer cm.mu.Unlock()

	if _, exists := cm.courses[courseID]; !exists {
		return fmt.Errorf("cours non trouvé: %s", courseID)
	}

	delete(cm.courses, courseID)

	cm.logger.Info("Cours supprimé",
		zap.String("course_id", courseID))

	return nil
}

// AddLesson ajoute une leçon à un cours
func (cm *CourseManager) AddLesson(ctx context.Context, courseID, title, description, content, videoURL string, duration time.Duration, order int, isFree bool) (*Lesson, error) {
	cm.mu.Lock()
	defer cm.mu.Unlock()

	course, exists := cm.courses[courseID]
	if !exists {
		return nil, fmt.Errorf("cours non trouvé: %s", courseID)
	}

	lessonID := uuid.New().String()
	lesson := &Lesson{
		ID:          lessonID,
		CourseID:    courseID,
		Title:       title,
		Description: description,
		Content:     content,
		VideoURL:    videoURL,
		Duration:    duration,
		Order:       order,
		IsFree:      isFree,
		CreatedAt:   time.Now(),
		UpdatedAt:   time.Now(),
	}

	course.Lessons = append(course.Lessons, lesson)
	course.UpdatedAt = time.Now()

	cm.logger.Info("Leçon ajoutée",
		zap.String("course_id", courseID),
		zap.String("lesson_id", lessonID),
		zap.String("title", title))

	return lesson, nil
}

// AddExercise ajoute un exercice à un cours
func (cm *CourseManager) AddExercise(ctx context.Context, courseID, lessonID, title, description, content, solution string, exerciseType ExerciseType, points int, timeLimit time.Duration, isRequired bool) (*Exercise, error) {
	cm.mu.Lock()
	defer cm.mu.Unlock()

	course, exists := cm.courses[courseID]
	if !exists {
		return nil, fmt.Errorf("cours non trouvé: %s", courseID)
	}

	exerciseID := uuid.New().String()
	exercise := &Exercise{
		ID:          exerciseID,
		CourseID:    courseID,
		LessonID:    lessonID,
		Title:       title,
		Description: description,
		Type:        exerciseType,
		Content:     content,
		Solution:    solution,
		Points:      points,
		TimeLimit:   timeLimit,
		IsRequired:  isRequired,
		CreatedAt:   time.Now(),
		UpdatedAt:   time.Now(),
	}

	course.Exercises = append(course.Exercises, exercise)
	course.UpdatedAt = time.Now()

	cm.logger.Info("Exercice ajouté",
		zap.String("course_id", courseID),
		zap.String("exercise_id", exerciseID),
		zap.String("title", title))

	return exercise, nil
}

// GetUserProgress récupère la progression d'un utilisateur dans un cours
func (cm *CourseManager) GetUserProgress(ctx context.Context, userID uuid.UUID, courseID string) (*CourseProgress, error) {
	cm.mu.RLock()
	defer cm.mu.RUnlock()

	progressKey := fmt.Sprintf("%s_%s", userID.String(), courseID)
	progress, exists := cm.progress[progressKey]
	if !exists {
		return nil, fmt.Errorf("progression non trouvée pour l'utilisateur %s dans le cours %s", userID, courseID)
	}

	return progress, nil
}

// UpdateUserProgress met à jour la progression d'un utilisateur
func (cm *CourseManager) UpdateUserProgress(ctx context.Context, userID uuid.UUID, courseID string, progress float64, completedLessons []string, currentLesson string, score float64, timeSpent time.Duration) (*CourseProgress, error) {
	cm.mu.Lock()
	defer cm.mu.Unlock()

	progressKey := fmt.Sprintf("%s_%s", userID.String(), courseID)

	userProgress, exists := cm.progress[progressKey]
	if !exists {
		userProgress = &CourseProgress{
			ID:               uuid.New().String(),
			UserID:           userID,
			CourseID:         courseID,
			Progress:         progress,
			CompletedLessons: completedLessons,
			CurrentLesson:    currentLesson,
			Score:            score,
			TimeSpent:        timeSpent,
			LastAccessed:     time.Now(),
			IsCompleted:      progress >= 1.0,
			CreatedAt:        time.Now(),
			UpdatedAt:        time.Now(),
		}
		cm.progress[progressKey] = userProgress
	} else {
		userProgress.Progress = progress
		userProgress.CompletedLessons = completedLessons
		userProgress.CurrentLesson = currentLesson
		userProgress.Score = score
		userProgress.TimeSpent = timeSpent
		userProgress.LastAccessed = time.Now()
		userProgress.IsCompleted = progress >= 1.0
		userProgress.UpdatedAt = time.Now()

		if userProgress.IsCompleted && userProgress.CompletedAt.IsZero() {
			userProgress.CompletedAt = time.Now()
		}
	}

	cm.logger.Info("Progression utilisateur mise à jour",
		zap.String("user_id", userID.String()),
		zap.String("course_id", courseID),
		zap.Float64("progress", progress))

	return userProgress, nil
}

// IssueCertificate émet un certificat pour un utilisateur
func (cm *CourseManager) IssueCertificate(ctx context.Context, courseID string, userID uuid.UUID, title, description string, score, maxScore float64) (*Certificate, error) {
	cm.mu.Lock()
	defer cm.mu.Unlock()

	certificateID := uuid.New().String()
	isPassed := score >= maxScore*0.7 // 70% pour réussir

	certificate := &Certificate{
		ID:          certificateID,
		CourseID:    courseID,
		UserID:      userID,
		Title:       title,
		Description: description,
		Score:       score,
		MaxScore:    maxScore,
		IsPassed:    isPassed,
		IssuedAt:    time.Now(),
		ExpiresAt:   time.Now().AddDate(2, 0, 0), // Valide 2 ans
		CreatedAt:   time.Now(),
	}

	// Ajouter le certificat au cours
	if course, exists := cm.courses[courseID]; exists {
		course.Certificates = append(course.Certificates, certificate)
		course.UpdatedAt = time.Now()
	}

	cm.logger.Info("Certificat émis",
		zap.String("certificate_id", certificateID),
		zap.String("course_id", courseID),
		zap.String("user_id", userID.String()),
		zap.Bool("is_passed", isPassed))

	return certificate, nil
}
