import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:drift/drift.dart';
import '../local/app_database.dart';
import '../remote/api_client.dart';
import '../../../../main.dart';
import 'user_repository.dart';

final workoutRepositoryProvider = Provider<WorkoutRepository>((ref) {
  final db = ref.watch(appDatabaseProvider);
  final apiClient = ref.watch(apiClientProvider);
  return WorkoutRepository(db: db, apiClient: apiClient);
});

class WorkoutRepository {
  final AppDatabase db;
  final ApiClient apiClient;

  WorkoutRepository({required this.db, required this.apiClient});

  Future<List<WorkoutCollection>> getAllWorkouts() async {
    return await db.select(db.workouts).get();
  }

  Future<List<WorkoutCollection>> getWorkoutsForCycle(String cycleId) async {
    return await (db.select(db.workouts)
          ..where((w) => w.cycleId.equals(cycleId)))
        .get();
  }

  Future<List<WorkoutCollection>> getCompletedWorkouts(String userId) async {
    return await (db.select(db.workouts)
          ..where((w) => w.userId.equals(userId) & w.completedAt.isNotNull()))
        .get();
  }

  Future<void> saveWorkout(WorkoutCollection workout) async {
    final companion = workout.toCompanion(true).copyWith(
          updatedAt: Value(DateTime.now()),
          isDirty: const Value(true),
        );
    await db.into(db.workouts).insertOnConflictUpdate(companion);
  }

  Future<WorkoutCollection> createWorkout({
    required String userId,
    required String cycleId,
    required int week,
    required int day,
    required List<dynamic> exercises,
  }) async {
    final companion = WorkoutsCompanion(
      userId: Value(userId),
      cycleId: Value(cycleId),
      week: Value(week),
      day: Value(day),
      exercises: Value(exercises),
      scheduledDate: Value(DateTime.now()),
      xpEarned: const Value(0),
      notes: const Value(''),
      isDirty: const Value(true),
      createdAt: Value(DateTime.now()),
      updatedAt: Value(DateTime.now()),
    );

    final id = await db.into(db.workouts).insert(companion);
    return await (db.select(db.workouts)..where((w) => w.id.equals(id)))
        .getSingle();
  }

  Future<void> completeWorkout(
    WorkoutCollection workout, {
    required int xpEarned,
  }) async {
    final companion = WorkoutsCompanion(
      id: Value(workout.id),
      completedAt: Value(DateTime.now()),
      xpEarned: Value(xpEarned),
      exercises: Value(workout.exercises),
      isDirty: const Value(true),
      updatedAt: Value(DateTime.now()),
    );

    await (db.update(db.workouts)..where((w) => w.id.equals(workout.id)))
        .write(companion);
  }

  Future<void> createCompletedWorkout({
    required String cycleId,
    required int week,
    required int day,
    required List<Map<String, dynamic>> exercises,
    required int xpEarned,
    required int durationSeconds,
  }) async {
    final now = DateTime.now();
    final companion = WorkoutsCompanion.insert(
      userId: (await apiClient.getUserId()) ?? 'local',
      cycleId: cycleId,
      week: week,
      day: day,
      exercises: exercises,
      xpEarned: Value(xpEarned),
      completedAt: Value(now),
      updatedAt: Value(now),
    );

    await db.into(db.workouts).insert(companion);
  }

  Future<WorkoutCollection?> getWorkoutByWeekDay({
    required String cycleId,
    String? localCycleId,
    required int week,
    required int day,
  }) async {
    return await (db.select(db.workouts)
          ..where((w) {
            final weekDayCheck = w.week.equals(week) & w.day.equals(day);

            Expression<bool> cycleCheck = w.cycleId.equals(cycleId);
            if (localCycleId != null) {
              cycleCheck = cycleCheck | w.cycleId.equals(localCycleId);
            }

            return weekDayCheck & cycleCheck;
          })
          ..limit(1))
        .getSingleOrNull();
  }

  Future<WorkoutCollection?> getWorkoutById(int id) async {
    return await (db.select(db.workouts)..where((w) => w.id.equals(id)))
        .getSingleOrNull();
  }
}
