package store

import (
	"database/sql"
	"fmt"
	"krafttrainer/internal/model"
)

// ListSets gibt alle nicht-gelöschten Sets mit ihren Übungen zurück.
func (s *Store) ListSets() ([]model.TrainingSet, error) {
	rows, err := s.db.Query(`
		SELECT id, name, created_at FROM training_sets
		WHERE deleted_at IS NULL ORDER BY name`)
	if err != nil {
		return nil, fmt.Errorf("Sets abfragen: %w", err)
	}
	defer rows.Close()

	var sets []model.TrainingSet
	for rows.Next() {
		var ts model.TrainingSet
		if err := rows.Scan(&ts.ID, &ts.Name, &ts.CreatedAt); err != nil {
			return nil, fmt.Errorf("Set scannen: %w", err)
		}
		sets = append(sets, ts)
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	if sets == nil {
		sets = []model.TrainingSet{}
	}

	for i := range sets {
		exercises, err := s.getSetExercises(sets[i].ID)
		if err != nil {
			return nil, err
		}
		sets[i].Exercises = exercises
	}
	return sets, nil
}

// GetSet gibt ein einzelnes Set mit Übungen zurück.
func (s *Store) GetSet(id int64) (*model.TrainingSet, error) {
	var ts model.TrainingSet
	err := s.db.QueryRow(`
		SELECT id, name, created_at, deleted_at FROM training_sets WHERE id = ?`, id,
	).Scan(&ts.ID, &ts.Name, &ts.CreatedAt, &ts.DeletedAt)
	if err == sql.ErrNoRows {
		return nil, nil
	}
	if err != nil {
		return nil, fmt.Errorf("Set abfragen: %w", err)
	}

	exercises, err := s.getSetExercises(id)
	if err != nil {
		return nil, err
	}
	ts.Exercises = exercises
	return &ts, nil
}

// CreateSet legt ein neues Set an (in einer Transaktion).
func (s *Store) CreateSet(req *model.CreateSetRequest) (*model.TrainingSet, error) {
	tx, err := s.db.Begin()
	if err != nil {
		return nil, fmt.Errorf("Transaktion starten: %w", err)
	}
	defer tx.Rollback()

	// Prüfen ob alle Übungen existieren
	for _, eid := range req.ExerciseIDs {
		var exists bool
		err := tx.QueryRow(`SELECT EXISTS(SELECT 1 FROM exercises WHERE id = ? AND deleted_at IS NULL)`, eid).Scan(&exists)
		if err != nil {
			return nil, fmt.Errorf("Übung prüfen: %w", err)
		}
		if !exists {
			return nil, fmt.Errorf("Übung %d existiert nicht", eid)
		}
	}

	result, err := tx.Exec(`INSERT INTO training_sets (name) VALUES (?)`, req.Name)
	if err != nil {
		return nil, fmt.Errorf("Set erstellen: %w", err)
	}

	id, _ := result.LastInsertId()

	for pos, eid := range req.ExerciseIDs {
		_, err := tx.Exec(`INSERT INTO set_exercises (set_id, exercise_id, position) VALUES (?, ?, ?)`, id, eid, pos)
		if err != nil {
			return nil, fmt.Errorf("Set-Übung zuordnen: %w", err)
		}
	}

	if err := tx.Commit(); err != nil {
		return nil, fmt.Errorf("Transaktion committen: %w", err)
	}
	return s.GetSet(id)
}

// UpdateSet aktualisiert ein Set (Name + Übungszuordnungen).
func (s *Store) UpdateSet(id int64, req *model.UpdateSetRequest) (*model.TrainingSet, error) {
	tx, err := s.db.Begin()
	if err != nil {
		return nil, fmt.Errorf("Transaktion starten: %w", err)
	}
	defer tx.Rollback()

	// Prüfen ob Set existiert
	var exists bool
	err = tx.QueryRow(`SELECT EXISTS(SELECT 1 FROM training_sets WHERE id = ? AND deleted_at IS NULL)`, id).Scan(&exists)
	if err != nil {
		return nil, fmt.Errorf("Set prüfen: %w", err)
	}
	if !exists {
		return nil, nil
	}

	// Prüfen ob alle Übungen existieren
	for _, eid := range req.ExerciseIDs {
		var eExists bool
		err := tx.QueryRow(`SELECT EXISTS(SELECT 1 FROM exercises WHERE id = ? AND deleted_at IS NULL)`, eid).Scan(&eExists)
		if err != nil {
			return nil, fmt.Errorf("Übung prüfen: %w", err)
		}
		if !eExists {
			return nil, fmt.Errorf("Übung %d existiert nicht", eid)
		}
	}

	_, err = tx.Exec(`UPDATE training_sets SET name = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?`, req.Name, id)
	if err != nil {
		return nil, fmt.Errorf("Set aktualisieren: %w", err)
	}

	_, err = tx.Exec(`DELETE FROM set_exercises WHERE set_id = ?`, id)
	if err != nil {
		return nil, fmt.Errorf("Set-Übungen löschen: %w", err)
	}

	for pos, eid := range req.ExerciseIDs {
		_, err := tx.Exec(`INSERT INTO set_exercises (set_id, exercise_id, position) VALUES (?, ?, ?)`, id, eid, pos)
		if err != nil {
			return nil, fmt.Errorf("Set-Übung zuordnen: %w", err)
		}
	}

	if err := tx.Commit(); err != nil {
		return nil, fmt.Errorf("Transaktion committen: %w", err)
	}
	return s.GetSet(id)
}

// SoftDeleteSet markiert ein Set als gelöscht.
func (s *Store) SoftDeleteSet(id int64) error {
	result, err := s.db.Exec(`
		UPDATE training_sets SET deleted_at = CURRENT_TIMESTAMP, updated_at = CURRENT_TIMESTAMP
		WHERE id = ? AND deleted_at IS NULL`, id,
	)
	if err != nil {
		return fmt.Errorf("Set löschen: %w", err)
	}
	rows, _ := result.RowsAffected()
	if rows == 0 {
		return sql.ErrNoRows
	}
	return nil
}

// getSetExercises lädt die Übungen eines Sets sortiert nach Position.
func (s *Store) getSetExercises(setID int64) ([]model.Exercise, error) {
	rows, err := s.db.Query(`
		SELECT e.id, e.name, e.description, e.muscle_group, e.weight_step_kg, e.created_at, e.updated_at
		FROM exercises e
		JOIN set_exercises se ON se.exercise_id = e.id
		WHERE se.set_id = ?
		ORDER BY se.position`, setID,
	)
	if err != nil {
		return nil, fmt.Errorf("Set-Übungen abfragen: %w", err)
	}
	defer rows.Close()

	var exercises []model.Exercise
	for rows.Next() {
		var e model.Exercise
		if err := rows.Scan(&e.ID, &e.Name, &e.Description, &e.MuscleGroup, &e.WeightStepKg, &e.CreatedAt, &e.UpdatedAt); err != nil {
			return nil, fmt.Errorf("Übung scannen: %w", err)
		}
		exercises = append(exercises, e)
	}
	if exercises == nil {
		exercises = []model.Exercise{}
	}
	return exercises, rows.Err()
}
