import os
import pickle
import logging
import threading
import time
from datetime import datetime
from typing import Dict, Any, List, Set

logger = logging.getLogger(__name__)

# Use an absolute path anchored to the project root (two levels up from this file)
# so the pickle is always written to the same place regardless of the working
# directory at startup time.
_PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(
    os.path.abspath(__file__)
))))
STATE_FILE = os.path.join(_PROJECT_ROOT, "data", "rider_active_state.pkl")
_FILE_LOCK = threading.Lock()


class RiderStateManager:
    """
    Manages the 'Short-Term' Active State of Riders for session persistence.
    Tracks:
    - Minutes Committed (Remaining Workload)
    - Active Kitchens (Unique Pickups in current queue)
    - Last Planned Drop Location (for Daisy Chaining)
    - Timestamp of last update (for Time Decay)
    """
    def __init__(self, state_file: str = STATE_FILE):
        self.state_file = state_file
        self.states = self._load_states()

    def _load_states(self) -> Dict[str, Any]:
        """Load states from pickle."""
        os.makedirs(os.path.dirname(self.state_file), exist_ok=True)
        if not os.path.exists(self.state_file):
            return {}
        try:
            with _FILE_LOCK:
                with open(self.state_file, 'rb') as f:
                    return pickle.load(f)
        except Exception as e:
            logger.error(f"Failed to load rider active states: {e}")
            return {}

    def _save_states(self):
        """Save states to pickle."""
        try:
            with _FILE_LOCK:
                with open(self.state_file, 'wb') as f:
                    pickle.dump(self.states, f)
        except Exception as e:
            logger.error(f"Failed to save rider active states: {e}")

    def get_rider_state(self, rider_id: int) -> Dict[str, Any]:
        """
        Get the current active state of a rider with TIME DECAY applied.
        If the server restarts after 30 mins, the 'minutes_committed' should reduce by 30.
        """
        rider_id = int(rider_id)
        raw_state = self.states.get(rider_id)
        
        if not raw_state:
            return {
                'minutes_remaining': 0.0,
                'last_drop_lat': None,
                'last_drop_lon': None,
                'active_kitchens': set(),
                'last_updated_ts': time.time()
            }
        
        # Apply Time Decay
        last_ts = raw_state.get('last_updated_ts', time.time())
        current_ts = time.time()
        elapsed_mins = (current_ts - last_ts) / 60.0
        
        remaining = max(0.0, raw_state.get('minutes_remaining', 0.0) - elapsed_mins)
        
        # If queue is empty, kitchens are cleared
        kitchens = raw_state.get('active_kitchens', set())
        if remaining <= 5.0: # Buffer: if almost done, free up kitchens
            kitchens = set()
            
        return {
            'minutes_remaining': remaining,
            'last_drop_lat': raw_state.get('last_drop_lat'),
            'last_drop_lon': raw_state.get('last_drop_lon'),
            'active_kitchens': kitchens,
            'last_updated_ts': current_ts
        }

    def update_rider_state(self, rider_id: int, added_minutes: float, new_kitchens: Set[str], last_lat: float, last_lon: float):
        """
        Update the state after a new assignment.
        """
        rider_id = int(rider_id)
        
        # Get current state (decayed)
        current = self.get_rider_state(rider_id)
        
        # Accumulate
        updated_minutes = current['minutes_remaining'] + added_minutes
        updated_kitchens = current['active_kitchens'].union(new_kitchens)
        
        self.states[rider_id] = {
            'minutes_remaining': updated_minutes,
            'last_drop_lat': last_lat,
            'last_drop_lon': last_lon,
            'active_kitchens': updated_kitchens,
            'last_updated_ts': time.time()
        }
        
        self._save_states()

    def clear_state(self, rider_id: int):
        rider_id = int(rider_id)
        if rider_id in self.states:
            del self.states[rider_id]
            self._save_states()
