import re
import os
from datetime import date
from pathlib import Path
from typing import Dict, List, Optional, Any
from dataclasses import dataclass

from health.services.manual_log_storage import ManualLogStorage
from health.models.manual_log import SupplementEntry, AlcoholEntry
from health.utils.logging_config import setup_logger

logger = setup_logger(__name__)

# Default Obsidian Daily Notes Path (can be overridden)
DEFAULT_OBSIDIAN_PATH = Path.home() / "Library/Mobile Documents/iCloud~md~obsidian/Documents/obsidian/daily"

# Mapping from Obsidian YAML keys to Supplement names
SUPPLEMENT_MAPPING = {
    "MED_001": "Medication 001",
    "nac": "NAC",
    "magnesium": "Magnesium",
    "fish_oil": "Fish Oil",
    "coq10": "CoQ10",
    "berberine": "Berberine",
    "b_complex": "Vitamin B Complex",
    "v_d3_k2": "Vitamin D3+K2"
}

# Mapping for Fasting Modes
FASTING_MAPPING = {
    "PSMF": "PSMF",
    "OMAD": "OMAD",
    "WaterFast": "Water Fast"
}

class ObsidianSyncService:
    """Service to sync data from Obsidian daily notes."""

    def __init__(self, obsidian_dir: Optional[Path] = None):
        self.obsidian_dir = obsidian_dir or DEFAULT_OBSIDIAN_PATH
        self.storage = ManualLogStorage()

    def _get_note_path(self, target_date: date) -> Path:
        """Get the path to the daily note for a given date."""
        filename = f"{target_date.isoformat()}.md"
        return self.obsidian_dir / filename

    def _parse_frontmatter(self, content: str) -> Dict[str, Any]:
        """Parse YAML frontmatter from markdown content manually."""
        frontmatter = {}
        
        # Regex to find content between ---
        match = re.search(r"^---\n(.*?)\n---", content, re.DOTALL)
        if not match:
            return frontmatter

        yaml_content = match.group(1)
        
        # Parse line by line
        for line in yaml_content.split('\n'):
            line = line.strip()
            if not line or line.startswith('#'):
                continue
            
            if ':' in line:
                key, value = line.split(':', 1)
                key = key.strip()
                value = value.strip()
                
                # Handle booleans
                if value.lower() == 'true':
                    value = True
                elif value.lower() == 'false':
                    value = False
                # Handle numbers
                elif value.replace('.', '', 1).isdigit():
                    try:
                        value = float(value) if '.' in value else int(value)
                    except ValueError:
                        pass
                # Handle nulls
                elif value.lower() in ('null', 'nil', ''):
                    value = None
                
                frontmatter[key] = value
                
        return frontmatter

    def sync_daily_note(self, target_date: date) -> bool:
        """Sync data from Obsidian for a specific date.
        
        Returns:
            True if data was found and synced, False otherwise.
        """
        note_path = self._get_note_path(target_date)
        if not note_path.exists():
            logger.warning(f"No Obsidian note found for {target_date} at {note_path}")
            return False

        try:
            with open(note_path, 'r', encoding='utf-8') as f:
                content = f.read()

            data = self._parse_frontmatter(content)
            if not data:
                logger.info(f"No frontmatter found in {note_path}")
                return False

            log = self.storage.load_log(target_date)
            changes_made = False

            # 1. Sync Fasting Mode
            for key, mode in FASTING_MAPPING.items():
                if data.get(key) is True:
                    if log.fasting_mode != mode:
                        log.fasting_mode = mode
                        changes_made = True
                        logger.info(f"Synced Fasting Mode: {mode}")
                    break # Only one fasting mode per day
            
            if data.get("fasting") is False and log.fasting_mode:
                # If explicitly false, maybe clear it? 
                # Decided to safer: only set if True. 
                pass

            # 2. Sync Supplements
            for key, name in SUPPLEMENT_MAPPING.items():
                if data.get(key) is True:
                    # Check if already exists
                    exists = any(
                        s.supplement_name == name and s.time == "00:00"
                        for s in log.supplement_entries
                    )
                    
                    if not exists:
                        entry = SupplementEntry(
                            time="00:00", # Default time
                            supplement_name=name,
                            dosage=None, # Dosage not tracked in Obsidian YAML boolean
                            timing="obsidian_sync"
                        )
                        log.supplement_entries.append(entry)
                        changes_made = True
                        logger.info(f"Synced Supplement: {name}")

            # 3. Sync Alcohol
            if data.get("alcohol") is True:
                # Check for existing alcohol entry from Obsidian (generic one)
                alcohol_ml = data.get("alcohol_ml", 0)
                amount_str = f"{alcohol_ml}ml pure alcohol" if alcohol_ml else "See Obsidian"
                
                # Check if we already have a 'wine' entry at 00:00 (our default)
                exists = any(
                    (a.time == "00:00" and a.type == "wine")
                    for a in log.alcohol_entries
                )
                
                if not exists:
                    entry = AlcoholEntry(
                        time="00:00",
                        type="wine", # Default type per user request
                        amount=amount_str,
                        notes=f"Synced from Obsidian (alcohol_ml: {alcohol_ml})"
                    )
                    log.alcohol_entries.append(entry)
                    changes_made = True
                    logger.info(f"Synced Alcohol: {amount_str}")

            if changes_made:
                self.storage.save_log(log)
                logger.info(f"Successfully synced Obsidian data for {target_date}")
                return True
            else:
                logger.info(f"No new data to sync for {target_date}")
                return True

        except Exception as e:
            logger.error(f"Failed to sync Obsidian note for {target_date}: {e}")
            return False
