import json
from datetime import datetime, timedelta
from pathlib import Path
from typing import Optional, List

from pywhooshconnect.common.mapper.base import PowerZonesOptions
from pywhooshconnect.garmin.client.GarminClient import GarminClient
from pywhooshconnect.garmin.mapper.garmin_to_generic_power_zones import (
    GarminToGenericPowerZonesMapper,
)
from pywhooshconnect.garmin.mapper.garmin_to_generic_workout import (
    GarminToGenericScheduledWorkoutMapper,
)
from pywhooshconnect.garmin.model.garmin_workout_dto import GarminSport
from pywhooshconnect.garmin.service.garmin_training_plan_service import (
    GarminTrainingPlanService,
)
from pywhooshconnect.mywhoosh.mapper.generic_to_mywhoosh import (
    GenericToMyWhooshWorkoutMapper,
)
from pywhooshconnect.mywhoosh.mapper.power_zones_config import PowerZoneConfig
from pywhooshconnect.mywhoosh.model.mywhoosh_workout_dto import MyWhooshWorkout


class GarminToMyWhooshWorkoutSyncService:
    garminClient: GarminClient
    garmin_training_plan_service: GarminTrainingPlanService

    def __init__(self, garmin_client: GarminClient):
        self.garminClient = garmin_client
        self.garmin_training_plan_service = GarminTrainingPlanService(self.garminClient)

    @classmethod
    def from_credentials(cls, email: str, password: str):
        client = GarminClient(email, password)
        client.login()
        return cls(client)

    def sync_workouts(
        self,
        sport: GarminSport,
        from_date: datetime = datetime.today(),
        to_date: Optional[datetime] = None,
        config_file: Optional[Path] = None,
    ) -> List[MyWhooshWorkout]:
        # Retrieve Garmin workouts
        to_date = to_date if to_date is not None else (from_date + timedelta(days=7))
        garmin_workouts = self.garmin_training_plan_service.get_scheduled_workouts(
            sport=sport, from_date=from_date, to_date=to_date
        )

        # Retrieve Garmin power zones and create PowerZonesOptions
        garmin_power_zones = self.garmin_training_plan_service.get_power_zones_by_sport(
            sport=sport
        )
        power_zones = GarminToGenericPowerZonesMapper().map(garmin_power_zones)
        config_file_str = str(config_file) if config_file is not None else None
        power_zones_config = PowerZoneConfig(config_path=config_file_str)
        power_zones_options = PowerZonesOptions(
            power_zones=power_zones, config=power_zones_config
        )

        # Map each Garmin workout to MyWhoosh format and return it
        mywhoosh_workouts = []
        for garmin_workout in garmin_workouts:
            generic_workout = GarminToGenericScheduledWorkoutMapper().map(
                garmin_workout, power_zones_options
            )
            mywhoosh_workout = GenericToMyWhooshWorkoutMapper().map(
                generic_workout, power_zones_options
            )
            mywhoosh_workouts.append(mywhoosh_workout)

        return mywhoosh_workouts

    def sync_and_download_workouts(
        self,
        sport: GarminSport,
        from_date: datetime = datetime.today(),
        to_date: Optional[datetime] = None,
        output_dir: str = "~/downloads/",
        config_file: Optional[Path] = None,
    ):
        mywhoosh_workouts = self.sync_workouts(sport, from_date, to_date, config_file)

        # Map each Garmin workout to MyWhoosh format and save it
        for mywhoosh_workout in mywhoosh_workouts:
            data = json.loads(mywhoosh_workout.to_json())

            output_dir = Path(output_dir).expanduser()
            filename = output_dir.joinpath(f"{mywhoosh_workout.Name}.json")
            with open(filename, "w", encoding="utf-8") as f:
                json.dump(data, f, ensure_ascii=False, indent=4)

            print(f"Saved {filename}")
