"""
Garmin API client wrapper for health data retrieval.

Supports Garmin Connect China (connect.garmin.cn) with authentication,
data fetching, error handling, and retry logic.
"""

import time
from datetime import date, datetime
from typing import Optional, Dict, Any, List
from pathlib import Path

from garminconnect import Garmin
from garth.exc import GarthHTTPError

from health import config
from health.utils.exceptions import (
    GarminAuthError,
    GarminAPIError,
    RateLimitError,
)
from health.utils.logging_config import setup_logger
from health.models.daily_metrics import (
    StepsData,
    HeartRateData,
    SleepData,
    StressData,
    BodyBatteryData,
    SpO2Data,
    RespirationData,
    HydrationData,
    FloorsData,
    IntensityMinutesData,
    HRVData,
    RHRData,
    LifestyleLoggingData,
)
from health.models.activity import Activity
from health.models.body_metrics import WeightData

logger = setup_logger(__name__)


class GarminHealthClient:
    """Client for fetching health data from Garmin Connect China."""

    def __init__(
        self,
        email: Optional[str] = None,
        password: Optional[str] = None,
        token_store: Optional[Path] = None,
    ) -> None:
        """Initialize Garmin client.

        Args:
            email: Garmin account email (defaults to config.GARMIN_EMAIL)
            password: Garmin account password (defaults to config.GARMIN_PASSWORD)
            token_store: Path to token storage directory (defaults to config.TOKEN_STORE)
        """
        self.email = email or config.GARMIN_EMAIL
        self.password = password or config.GARMIN_PASSWORD
        self.token_store = token_store or config.TOKEN_STORE

        if not self.email or not self.password:
            config.validate_credentials()

        self.client: Optional[Garmin] = None
        self._authenticated = False

    def authenticate(self) -> None:
        """Authenticate with Garmin Connect China.

        Raises:
            GarminAuthError: If authentication fails
        """
        logger.info(f"Authenticating with Garmin Connect China for {self.email}")

        try:
            # Initialize Garmin client for China region
            self.client = Garmin(
                email=self.email,
                password=self.password,
                is_cn=True,  # China region support
            )

            # Check if token files exist for first-time login handling
            oauth1_token_path = self.token_store / "oauth1_token.json"
            oauth2_token_path = self.token_store / "oauth2_token.json"
            tokens_exist = oauth1_token_path.exists() and oauth2_token_path.exists()

            if tokens_exist:
                logger.debug("Found existing tokens, attempting to load them")
            else:
                logger.debug("No existing tokens found, will use credential-based login")

            # Login and save tokens
            # garminconnect will try to load tokens if they exist, or use credentials
            self.client.login(tokenstore=str(self.token_store))

            # Save tokens after successful login (especially important for first login)
            if not tokens_exist:
                logger.debug("Saving tokens for future use")
                self.client.garth.dump(str(self.token_store))

            self._authenticated = True

            logger.info("Successfully authenticated with Garmin Connect China")

        except GarthHTTPError as e:
            logger.error(f"Garmin authentication failed: {e}")
            raise GarminAuthError(f"Authentication failed: {e}") from e
        except FileNotFoundError as e:
            # Handle case where tokenstore directory or token files don't exist
            logger.warning(f"Token files not found, retrying with credential-based login: {e}")
            try:
                # Retry without tokenstore to force credential-based login
                self.client.login()
                # Save tokens for future use
                self.client.garth.dump(str(self.token_store))
                self._authenticated = True
                logger.info("Successfully authenticated with credentials and saved tokens")
            except Exception as retry_error:
                logger.error(f"Retry authentication failed: {retry_error}")
                raise GarminAuthError(f"Authentication failed: {retry_error}") from retry_error
        except Exception as e:
            logger.error(f"Unexpected authentication error: {e}")
            raise GarminAuthError(f"Unexpected authentication error: {e}") from e

    def _ensure_authenticated(self) -> None:
        """Ensure client is authenticated before making API calls.

        Raises:
            GarminAuthError: If not authenticated
        """
        if not self._authenticated or not self.client:
            raise GarminAuthError(
                "Client not authenticated. Call authenticate() first."
            )

    def _retry_api_call(
        self, func, *args, max_retries: int = config.MAX_RETRIES, **kwargs
    ) -> Any:
        """Retry API call with exponential backoff.

        Args:
            func: Function to call
            max_retries: Maximum retry attempts
            *args: Positional arguments for func
            **kwargs: Keyword arguments for func

        Returns:
            Result of function call

        Raises:
            GarminAPIError: If all retries fail
            RateLimitError: If rate limit is exceeded
        """
        for attempt in range(max_retries):
            try:
                return func(*args, **kwargs)

            except GarthHTTPError as e:
                # Check for rate limiting
                if e.status == 429:
                    raise RateLimitError(f"API rate limit exceeded: {e}") from e

                # Retry on server errors (5xx) or specific client errors
                if attempt < max_retries - 1 and (
                    e.status >= 500 or e.status in [408, 429]
                ):
                    delay = config.RETRY_DELAY_SECONDS * (2**attempt)
                    logger.warning(
                        f"API call failed (attempt {attempt + 1}/{max_retries}): {e}. "
                        f"Retrying in {delay}s..."
                    )
                    time.sleep(delay)
                    continue

                # Non-retryable error
                raise GarminAPIError(f"API call failed: {e}", status_code=e.status) from e

            except Exception as e:
                if attempt < max_retries - 1:
                    delay = config.RETRY_DELAY_SECONDS * (2**attempt)
                    logger.warning(
                        f"Unexpected error (attempt {attempt + 1}/{max_retries}): {e}. "
                        f"Retrying in {delay}s..."
                    )
                    time.sleep(delay)
                    continue

                raise GarminAPIError(f"Unexpected API error: {e}") from e

        raise GarminAPIError(f"Max retries ({max_retries}) exceeded")

    # ============ Daily Metrics Fetchers ============

    def fetch_steps(self, target_date: date) -> Optional[StepsData]:
        """Fetch daily step data.

        Args:
            target_date: Date to fetch data for

        Returns:
            StepsData model or None if no data available
        """
        self._ensure_authenticated()
        logger.debug(f"Fetching steps data for {target_date}")

        try:
            # Use get_daily_steps to get daily summary instead of 15-min intervals
            raw_data_list = self._retry_api_call(
                self.client.get_daily_steps,
                target_date.isoformat(),
                target_date.isoformat()
            )

            if not raw_data_list or not isinstance(raw_data_list, list) or len(raw_data_list) == 0:
                return None

            raw_data = raw_data_list[0]

            return StepsData(
                date=target_date,
                total_steps=raw_data.get("totalSteps"),
                total_distance_meters=raw_data.get("totalDistance"),
                calories_burned=raw_data.get("calories"),  # Note: might not be in response
                step_goal=raw_data.get("stepGoal"),
                raw_data=raw_data,
            )

        except GarminAPIError:
            raise
        except Exception as e:
            logger.error(f"Error parsing steps data: {e}")
            return None

    def fetch_heart_rate(self, target_date: date) -> Optional[HeartRateData]:
        """Fetch daily heart rate data.

        Args:
            target_date: Date to fetch data for

        Returns:
            HeartRateData model or None if no data available
        """
        self._ensure_authenticated()
        logger.debug(f"Fetching heart rate data for {target_date}")

        try:
            raw_data = self._retry_api_call(
                self.client.get_heart_rates, target_date.isoformat()
            )

            if not raw_data:
                return None

            return HeartRateData(
                date=target_date,
                resting_heart_rate=raw_data.get("restingHeartRate"),
                min_heart_rate=raw_data.get("minHeartRate"),
                max_heart_rate=raw_data.get("maxHeartRate"),
                average_heart_rate=raw_data.get("averageHeartRate"),
                heart_rate_zones=raw_data.get("heartRateZones"),
                raw_data=raw_data,
            )

        except GarminAPIError:
            raise
        except Exception as e:
            logger.error(f"Error parsing heart rate data: {e}")
            return None

    def fetch_sleep(self, target_date: date) -> Optional[SleepData]:
        """Fetch daily sleep data.

        Args:
            target_date: Date to fetch data for

        Returns:
            SleepData model or None if no data available
        """
        self._ensure_authenticated()
        logger.debug(f"Fetching sleep data for {target_date}")

        try:
            raw_data = self._retry_api_call(
                self.client.get_sleep_data, target_date.isoformat()
            )

            if not raw_data or "dailySleepDTO" not in raw_data:
                return None

            sleep_dto = raw_data["dailySleepDTO"]

            return SleepData(
                date=target_date,
                total_sleep_seconds=sleep_dto.get("sleepTimeSeconds"),
                deep_sleep_seconds=sleep_dto.get("deepSleepSeconds"),
                light_sleep_seconds=sleep_dto.get("lightSleepSeconds"),
                rem_sleep_seconds=sleep_dto.get("remSleepSeconds"),
                awake_seconds=sleep_dto.get("awakeSleepSeconds"),
                sleep_score=sleep_dto.get("sleepScores", {}).get("overall", {}).get("value"),
                sleep_levels=raw_data.get("sleepLevels"),
                raw_data=raw_data,
            )

        except GarminAPIError:
            raise
        except Exception as e:
            logger.error(f"Error parsing sleep data: {e}")
            return None

    def fetch_stress(self, target_date: date) -> Optional[StressData]:
        """Fetch daily stress data.

        Args:
            target_date: Date to fetch data for

        Returns:
            StressData model or None if no data available
        """
        self._ensure_authenticated()
        logger.debug(f"Fetching stress data for {target_date}")

        try:
            raw_data = self._retry_api_call(
                self.client.get_stress_data, target_date.isoformat()
            )

            if not raw_data:
                return None

            return StressData(
                date=target_date,
                average_stress_level=raw_data.get("avgStressLevel"),
                max_stress_level=raw_data.get("maxStressLevel"),
                rest_stress_duration_seconds=raw_data.get("restStressDuration"),
                activity_stress_duration_seconds=raw_data.get("activityStressDuration"),
                low_stress_duration_seconds=raw_data.get("lowStressDuration"),
                medium_stress_duration_seconds=raw_data.get("mediumStressDuration"),
                high_stress_duration_seconds=raw_data.get("highStressDuration"),
                stress_timeline=raw_data.get("stressValuesArray"),
                raw_data=raw_data,
            )

        except GarminAPIError:
            raise
        except Exception as e:
            logger.error(f"Error parsing stress data: {e}")
            return None

    def fetch_body_battery(self, target_date: date) -> Optional[BodyBatteryData]:
        """Fetch daily Body Battery data.

        Args:
            target_date: Date to fetch data for

        Returns:
            BodyBatteryData model or None if no data available
        """
        self._ensure_authenticated()
        logger.debug(f"Fetching Body Battery data for {target_date}")

        try:
            # API requires startdate and enddate, returns list[dict]
            raw_data_list = self._retry_api_call(
                self.client.get_body_battery,
                target_date.isoformat(),
                target_date.isoformat()  # enddate same as startdate for single day
            )

            if not raw_data_list or not isinstance(raw_data_list, list) or len(raw_data_list) == 0:
                return None

            raw_data = raw_data_list[0]

            return BodyBatteryData(
                date=target_date,
                charged=raw_data.get("charged"),
                drained=raw_data.get("drained"),
                highest_value=raw_data.get("highestBodyBatteryValue"),
                lowest_value=raw_data.get("lowestBodyBatteryValue"),
                most_recent_value=raw_data.get("mostRecentValue"),
                timeline=raw_data.get("bodyBatteryValuesArray"),
                raw_data=raw_data,
            )

        except GarminAPIError:
            raise
        except Exception as e:
            logger.error(f"Error parsing Body Battery data: {e}")
            return None

    def fetch_activities(
        self, start_date: date, end_date: date
    ) -> List[Activity]:
        """Fetch activities within a date range.

        Args:
            start_date: Start date
            end_date: End date

        Returns:
            List of Activity models
        """
        self._ensure_authenticated()
        logger.debug(f"Fetching activities from {start_date} to {end_date}")

        try:
            activities_data = self._retry_api_call(
                self.client.get_activities_by_date,
                start_date.isoformat(),
                end_date.isoformat(),
            )

            if not activities_data:
                return []

            activities = []
            for raw_activity in activities_data:
                try:
                    activity = Activity(
                        activity_id=str(raw_activity.get("activityId")),
                        activity_type=raw_activity.get("activityType", {}).get("typeKey", "unknown"),
                        activity_name=raw_activity.get("activityName"),
                        date=datetime.fromisoformat(
                            raw_activity.get("startTimeLocal").replace("Z", "")
                        ).date(),
                        start_time=datetime.fromisoformat(
                            raw_activity.get("startTimeLocal").replace("Z", "")
                        ),
                        duration_seconds=raw_activity.get("duration"),
                        distance_meters=raw_activity.get("distance"),
                        elevation_gain_meters=raw_activity.get("elevationGain"),
                        elevation_loss_meters=raw_activity.get("elevationLoss"),
                        calories=raw_activity.get("calories"),
                        average_heart_rate=raw_activity.get("averageHR"),
                        max_heart_rate=raw_activity.get("maxHR"),
                        average_speed_mps=raw_activity.get("averageSpeed"),
                        max_speed_mps=raw_activity.get("maxSpeed"),
                        average_cadence=raw_activity.get("averageRunningCadenceInStepsPerMinute"),
                        raw_data=raw_activity,
                    )
                    activities.append(activity)

                except Exception as e:
                    logger.warning(f"Failed to parse activity: {e}")
                    continue

            logger.info(f"Fetched {len(activities)} activities")
            return activities

        except GarminAPIError:
            raise
        except Exception as e:
            logger.error(f"Error fetching activities: {e}")
            return []

    def fetch_weight(self, target_date: date) -> Optional[WeightData]:
        """Fetch weight data for a specific date.

        Args:
            target_date: Date to fetch data for

        Returns:
            WeightData model or None if no data available
        """
        self._ensure_authenticated()
        logger.debug(f"Fetching weight data for {target_date}")

        try:
            # API requires startdate and enddate parameters
            raw_data = self._retry_api_call(
                self.client.get_weigh_ins,
                target_date.isoformat(),
                target_date.isoformat()  # enddate same as startdate for single day
            )

            if not raw_data or "dailyWeightSummaries" not in raw_data:
                return None

            summaries = raw_data["dailyWeightSummaries"]
            if not summaries:
                return None

            # Use the first (most recent) summary
            summary = summaries[0]

            # Weight info is in latestWeight, not directly in summary
            latest_weight = summary.get("latestWeight", summary)  # Fallback to summary if latestWeight doesn't exist

            return WeightData(
                date=target_date,
                weight_kg=latest_weight.get("weight") / 1000 if latest_weight.get("weight") else None,  # Convert grams to kg
                bmi=latest_weight.get("bmi"),
                body_fat_percentage=latest_weight.get("bodyFat"),
                body_water_percentage=latest_weight.get("bodyWater"),
                bone_mass_kg=latest_weight.get("boneMass") / 1000 if latest_weight.get("boneMass") else None,
                muscle_mass_kg=latest_weight.get("muscleMass") / 1000 if latest_weight.get("muscleMass") else None,
                source=latest_weight.get("sourceType"),
                raw_data=raw_data,
            )

        except GarminAPIError:
            raise
        except Exception as e:
            logger.error(f"Error parsing weight data: {e}")
            return None

    # Note: Additional metrics (SpO2, Respiration, Hydration, Floors, etc.)
    # may require specific API methods that might not be available in garminconnect library.
    # These would need to be implemented based on available API endpoints.
    # For now, we'll create placeholder methods that can be filled in later.

    def fetch_spo2(self, target_date: date) -> Optional[SpO2Data]:
        """Fetch SpO2 (blood oxygen saturation) data.

        Args:
            target_date: Date to fetch data for

        Returns:
            SpO2Data model or None if no data available
        """
        self._ensure_authenticated()
        logger.debug(f"Fetching SpO2 data for {target_date}")

        try:
            raw_data = self._retry_api_call(
                self.client.get_spo2_data, target_date.isoformat()
            )

            if not raw_data:
                return None

            # Extract SpO2 values from the API response
            # The API typically returns average, min, max values
            return SpO2Data(
                date=target_date,
                average_spo2=raw_data.get("averageSpO2"),
                min_spo2=raw_data.get("lowestSpO2"),
                max_spo2=raw_data.get("latestSpO2"),
                readings=raw_data.get("spO2ValueDescriptorsDTOList"),
                raw_data=raw_data,
            )

        except GarminAPIError:
            raise
        except Exception as e:
            logger.error(f"Error parsing SpO2 data: {e}")
            return None

    def fetch_respiration(self, target_date: date) -> Optional[RespirationData]:
        """Fetch respiration rate data.

        Args:
            target_date: Date to fetch data for

        Returns:
            RespirationData model or None if no data available
        """
        self._ensure_authenticated()
        logger.debug(f"Fetching respiration data for {target_date}")

        try:
            raw_data = self._retry_api_call(
                self.client.get_respiration_data, target_date.isoformat()
            )

            if not raw_data:
                return None

            # Extract respiration rate values from the API response
            return RespirationData(
                date=target_date,
                average_respiration_rate=raw_data.get("avgWakingRespirationValue"),
                min_respiration_rate=raw_data.get("lowestRespirationValue"),
                max_respiration_rate=raw_data.get("highestRespirationValue"),
                raw_data=raw_data,
            )

        except GarminAPIError:
            raise
        except Exception as e:
            logger.error(f"Error parsing respiration data: {e}")
            return None

    def fetch_hydration(self, target_date: date) -> Optional[HydrationData]:
        """Fetch hydration tracking data.

        Args:
            target_date: Date to fetch data for

        Returns:
            HydrationData model or None if no data available
        """
        self._ensure_authenticated()
        logger.debug(f"Fetching hydration data for {target_date}")

        try:
            raw_data = self._retry_api_call(
                self.client.get_hydration_data, target_date.isoformat()
            )

            if not raw_data:
                return None

            # Extract hydration values from the API response
            return HydrationData(
                date=target_date,
                total_intake_ml=raw_data.get("valueInML"),
                goal_ml=raw_data.get("sweatLossInML"),
                raw_data=raw_data,
            )

        except GarminAPIError:
            raise
        except Exception as e:
            logger.error(f"Error parsing hydration data: {e}")
            return None

    def fetch_floors(self, target_date: date) -> Optional[FloorsData]:
        """Fetch floors climbed data.

        Args:
            target_date: Date to fetch data for

        Returns:
            FloorsData model or None if no data available
        """
        self._ensure_authenticated()
        logger.debug(f"Fetching floors data for {target_date}")

        try:
            raw_data = self._retry_api_call(
                self.client.get_floors, target_date.isoformat()
            )

            if not raw_data:
                return None

            # Extract floors data from the API response
            # Floors data is in floorValuesArray with format [startTime, endTime, floorsAscended, floorsDescended]
            floors_climbed = 0
            floors_descended = 0
            floor_values_array = raw_data.get("floorValuesArray", [])

            # Sum up all floors from the array
            for entry in floor_values_array:
                if entry and len(entry) >= 4:
                    floors_climbed += entry[2] if entry[2] else 0
                    floors_descended += entry[3] if entry[3] else 0

            return FloorsData(
                date=target_date,
                floors_climbed=int(floors_climbed) if floors_climbed > 0 else None,
                floors_descended=int(floors_descended) if floors_descended > 0 else None,
                floor_goal=raw_data.get("userFloorsAscendedGoal"),
                raw_data=raw_data,
            )

        except GarminAPIError:
            raise
        except Exception as e:
            logger.error(f"Error parsing floors data: {e}")
            return None

    def fetch_intensity_minutes(self, target_date: date) -> Optional[IntensityMinutesData]:
        """Fetch intensity minutes data.

        Args:
            target_date: Date to fetch data for

        Returns:
            IntensityMinutesData model or None if no data available
        """
        self._ensure_authenticated()
        logger.debug(f"Fetching intensity minutes data for {target_date}")

        try:
            raw_data = self._retry_api_call(
                self.client.get_intensity_minutes_data, target_date.isoformat()
            )

            if not raw_data:
                return None

            # Extract intensity minutes from the API response
            moderate_minutes = raw_data.get("moderateMinutes")
            vigorous_minutes = raw_data.get("vigorousMinutes")

            # Calculate total minutes
            total_minutes = None
            if moderate_minutes is not None and vigorous_minutes is not None:
                total_minutes = moderate_minutes + vigorous_minutes * 2  # Vigorous counts as 2x

            return IntensityMinutesData(
                date=target_date,
                moderate_minutes=moderate_minutes,
                vigorous_minutes=vigorous_minutes,
                total_minutes=total_minutes,
                weekly_goal=raw_data.get("weekGoal"),
                raw_data=raw_data,
            )

        except GarminAPIError:
            raise
        except Exception as e:
            logger.error(f"Error parsing intensity minutes data: {e}")
            return None

    def fetch_hrv(self, target_date: date) -> Optional[HRVData]:
        """Fetch Heart Rate Variability (HRV) data.

        Args:
            target_date: Date to fetch data for

        Returns:
            HRVData model or None if no data available
        """
        self._ensure_authenticated()
        logger.debug(f"Fetching HRV data for {target_date}")

        try:
            raw_data = self._retry_api_call(
                self.client.get_hrv_data, target_date.isoformat()
            )

            if not raw_data:
                return None

            # Extract HRV values from the API response
            # Data is nested under "hrvSummary" in the response
            hrv_summary = raw_data.get("hrvSummary", {})
            baseline = hrv_summary.get("baseline", {}) if isinstance(hrv_summary.get("baseline"), dict) else {}

            return HRVData(
                date=target_date,
                hrv_value=hrv_summary.get("lastNightAvg"),
                baseline_hrv=baseline.get("lowUpper"),
                status=hrv_summary.get("status"),
                raw_data=raw_data,
            )

        except GarminAPIError:
            raise
        except Exception as e:
            logger.error(f"Error parsing HRV data: {e}")
            return None

    def fetch_rhr(self, target_date: date) -> Optional[RHRData]:
        """Fetch resting heart rate data.

        Args:
            target_date: Date to fetch data for

        Returns:
            RHRData model or None
        """
        self._ensure_authenticated()
        logger.debug(f"Fetching RHR data for {target_date}")

        try:
            raw_data = self._retry_api_call(
                self.client.get_rhr_day, target_date.isoformat()
            )

            if not raw_data:
                return None

            # Extract RHR from nested metrics map structure
            resting_heart_rate = None
            all_metrics = raw_data.get("allMetrics", {})
            metrics_map = all_metrics.get("metricsMap", {})
            rhr_metrics = metrics_map.get("WELLNESS_RESTING_HEART_RATE", [])

            if rhr_metrics and len(rhr_metrics) > 0:
                resting_heart_rate = int(rhr_metrics[0].get("value", 0))

            return RHRData(
                date=target_date,
                resting_heart_rate=resting_heart_rate,
                raw_data=raw_data,
            )

        except GarminAPIError:
            raise
        except Exception as e:
            logger.error(f"Error parsing RHR data: {e}")
            return None

    def fetch_lifestyle_logging(self, target_date: date) -> Optional[LifestyleLoggingData]:
        """Fetch lifestyle logging data from Garmin Lifestyle Logging feature.

        Parses alcohol, caffeine, meal quality/timing, light exercise, and
        intermittent fasting behaviors logged by the user in Garmin Connect.

        Args:
            target_date: Date to fetch data for

        Returns:
            LifestyleLoggingData model or None if no data available
        """
        self._ensure_authenticated()
        logger.debug(f"Fetching lifestyle logging data for {target_date}")

        try:
            raw_data = self._retry_api_call(
                self.client.get_lifestyle_logging_data, target_date.isoformat()
            )

            if not raw_data:
                return None

            logs = raw_data.get("dailyLogsReport", [])
            if not logs:
                return None

            # Return None if nothing was actually logged today (API always returns
            # all 8 template entries regardless; logStatus="YES" marks actual logs)
            any_logged = any(e.get("logStatus") == "YES" for e in logs)
            if not any_logged:
                return None

            # Helper: find a behavior entry by name and check if it was logged
            def _find(name: str) -> Optional[dict]:
                for entry in logs:
                    if entry.get("name") == name and entry.get("logStatus") == "YES":
                        return entry
                return None

            # Helper: sum amounts for a specific subtype name within an entry
            def _amount(entry: dict, subtype: str) -> int:
                for d in entry.get("details", []):
                    if d.get("subTypeName") == subtype:
                        return int(d.get("amount", 0))
                return 0

            alcohol_entry = _find("Alcohol")
            morning_caf_entry = _find("Morning Caffeine")
            late_caf_entry = _find("Late Caffeine")

            return LifestyleLoggingData(
                date=target_date,
                # Alcohol
                alcohol_logged=alcohol_entry is not None,
                alcohol_beer=_amount(alcohol_entry, "BEER") if alcohol_entry else 0,
                alcohol_wine=_amount(alcohol_entry, "WINE") if alcohol_entry else 0,
                alcohol_spirit=_amount(alcohol_entry, "SPIRIT") if alcohol_entry else 0,
                alcohol_other=_amount(alcohol_entry, "OTHER") if alcohol_entry else 0,
                # Morning caffeine
                morning_caffeine_logged=morning_caf_entry is not None,
                morning_caffeine_coffee=_amount(morning_caf_entry, "COFFEE") if morning_caf_entry else 0,
                morning_caffeine_tea=_amount(morning_caf_entry, "TEA") if morning_caf_entry else 0,
                morning_caffeine_other=_amount(morning_caf_entry, "OTHER") if morning_caf_entry else 0,
                # Late caffeine
                late_caffeine_logged=late_caf_entry is not None,
                late_caffeine_coffee=_amount(late_caf_entry, "COFFEE") if late_caf_entry else 0,
                late_caffeine_tea=_amount(late_caf_entry, "TEA") if late_caf_entry else 0,
                late_caffeine_other=_amount(late_caf_entry, "OTHER") if late_caf_entry else 0,
                # Boolean behaviors
                light_exercise=_find("Light Exercise") is not None,
                healthy_meals=_find("Healthy Meals") is not None,
                heavy_meals=_find("Heavy Meals") is not None,
                late_meals=_find("Late Meals") is not None,
                intermittent_fasting=_find("Intermittent Fasting") is not None,
                raw_data=raw_data,
            )

        except GarminAPIError:
            raise
        except Exception as e:
            logger.error(f"Error parsing lifestyle logging data: {e}")
            return None
