from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
import pandas as pd

class Calendar:
    def __init__(self, db_connection_method):
        self.execute = db_connection_method

    def fetch_workouts_for_person(self, person_id, date, view):
        prev_date, next_date = None, None
        if view == 'month':
            first_day_of_month = date.replace(day=1)
            days_to_subtract = (first_day_of_month.weekday() + 1) % 7
            start_date = first_day_of_month - timedelta(days=days_to_subtract)
            end_date = start_date + timedelta(days=6 * 7 - 1)
            prev_date = first_day_of_month - relativedelta(months=1)
            next_date = first_day_of_month + relativedelta(months=1)
        elif view == 'year':
            start_date = date.replace(month=1, day=1)
            end_date = date.replace(year=date.year + 1, month=1, day=1) - timedelta(days=1)
            prev_date = date - relativedelta(years=1)
            next_date = date + relativedelta(years=1)
        else:
            raise ValueError('Invalid view')

        query = """
        SELECT 
            w.workout_id, 
            w.start_date, 
            t.topset_id, 
            t.repetitions, 
            t.weight, 
            e.name AS exercise_name, 
            p.name AS person_name
        FROM 
            person p
            LEFT JOIN workout w ON p.person_id = w.person_id AND w.start_date BETWEEN %s AND %s
            LEFT JOIN topset t ON w.workout_id = t.workout_id
            LEFT JOIN exercise e ON t.exercise_id = e.exercise_id
        WHERE 
            p.person_id = %s
        ORDER BY 
            w.start_date, 
            t.topset_id;
        """
        workouts_data = self.execute(query, [start_date, end_date, person_id])

        # Assuming person_name is the same for all rows as we filter by person_id
        person_name = workouts_data[0]['person_name'] if workouts_data else 'Unknown'

        calendar_view = {'prev_date': prev_date, 'next_date': next_date, 'person_id': person_id, 'person_name': person_name, 'view': view, 'date': date}

        if view == 'month':
            calendar_view['days'] = []
            workouts_by_date = {}

            for row in workouts_data:
                if row['workout_id'] is None:
                    continue  # Skip rows that don't have workout data

                workout_date_str = row['start_date'].strftime("%Y-%m-%d")
                workout_id = row['workout_id']

                if workout_date_str not in workouts_by_date:
                    workouts_by_date[workout_date_str] = {}

                if workout_id not in workouts_by_date[workout_date_str]:
                    workouts_by_date[workout_date_str][workout_id] = {
                        'workout_id': workout_id,
                        'start_date': row['start_date'],
                        'sets': []
                    }

                if row['topset_id']:
                    workouts_by_date[workout_date_str][workout_id]['sets'].append({
                        'repetitions': row['repetitions'],
                        'weight': row['weight'],
                        'exercise_name': row['exercise_name']
                    })

            for current_date in pd.date_range(start_date, end_date, freq='D'):
                date_str = current_date.strftime("%Y-%m-%d")
                day_workouts = workouts_by_date.get(date_str, {})
                today = datetime.today().date()

                calendar_view['days'].append({
                    'day': current_date.day,
                    'is_today': current_date == today,
                    'is_in_current_month': current_date.month == date.month,  # Ensure it compares with the selected month
                    'has_workouts': len(day_workouts) > 0,
                    'workouts': list(day_workouts.values())
                })


        elif view == 'year':
            calendar_view['months'] = []
            workouts_by_date = {}
            for row in workouts_data:
                if row['start_date'] is None:
                    continue  # Skip rows that don't have workout data
                workout_date_str = row['start_date'].strftime("%Y-%m-%d")
                if workout_date_str not in workouts_by_date:
                    workouts_by_date[workout_date_str] = []

                workouts_by_date[workout_date_str].append({
                    'workout_id': row['workout_id'],
                    'start_date': row['start_date'],
                    'topset_id': row['topset_id'],
                    'repetitions': row['repetitions'],
                    'weight': row['weight'],
                    'exercise_name': row['exercise_name']
                })


            for month in range(1, 13):
                first_day_of_month = date.replace(month=month, day=1)
                days_to_subtract = (first_day_of_month.weekday() + 1) % 7
                start_date = first_day_of_month - timedelta(days=days_to_subtract)
                end_date = start_date + timedelta(days=6 * 7 - 1)

                month_data = {'name': first_day_of_month.strftime('%B'), 'first_day_of_month': first_day_of_month, 'days': []}

                current_day = start_date
                while current_day <= end_date:
                    day_workouts = workouts_by_date.get(current_day.strftime('%Y-%m-%d'), [])
                    has_workouts = len(day_workouts) > 0
                    first_workout_id = day_workouts[0]['workout_id'] if has_workouts else None

                    day_data = {
                        'day': current_day.day,
                        'is_today': current_day == datetime.today().date(),
                        'is_in_current_month': current_day.month == month,
                        'workouts': day_workouts,
                        'has_workouts': has_workouts,
                        'first_workout_id': first_workout_id
                    }
                    month_data['days'].append(day_data)
                    current_day += timedelta(days=1)

                calendar_view['months'].append(month_data)

        return calendar_view
